]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 31 Mar 2012 20:42:57 +0000 (13:42 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 31 Mar 2012 20:42:57 +0000 (13:42 -0700)
Pull second try at vfs part d#2 from Al Viro:
 "Miklos' first series (with do_lookup() rewrite split into edible
  chunks) + assorted bits and pieces.

  The 'untangling of do_lookup()' series is is a splitup of what used to
  be a monolithic patch from Miklos, so this series is basically "how do
  I convince myself that his patch is correct (or find a hole in it)".
  No holes found and I like the resulting cleanup, so in it went..."

Changes from try 1: Fix a boot problem with selinux, and commit messages
prettied up a bit.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (24 commits)
  vfs: fix out-of-date dentry_unhash() comment
  vfs: split __lookup_hash
  untangling do_lookup() - take __lookup_hash()-calling case out of line.
  untangling do_lookup() - switch to calling __lookup_hash()
  untangling do_lookup() - merge d_alloc_and_lookup() callers
  untangling do_lookup() - merge failure exits in !dentry case
  untangling do_lookup() - massage !dentry case towards __lookup_hash()
  untangling do_lookup() - get rid of need_reval in !dentry case
  untangling do_lookup() - eliminate a loop.
  untangling do_lookup() - expand the area under ->i_mutex
  untangling do_lookup() - isolate !dentry stuff from the rest of it.
  vfs: move MAY_EXEC check from __lookup_hash()
  vfs: don't revalidate just looked up dentry
  vfs: fix d_need_lookup/d_revalidate order in do_lookup
  ext3: move headers to fs/ext3/
  migrate ext2_fs.h guts to fs/ext2/ext2.h
  new helper: ext2_image_size()
  get rid of pointless includes of ext2_fs.h
  ext2: No longer export ext2_fs.h to user space
  mtdchar: kill persistently held vfsmount
  ...

646 files changed:
Documentation/ABI/stable/sysfs-driver-usb-usbtmc
Documentation/ABI/testing/sysfs-bus-event_source-devices-format [new file with mode: 0644]
Documentation/ABI/testing/sysfs-firmware-acpi
Documentation/CodingStyle
Documentation/acpi/apei/einj.txt
Documentation/aoe/aoe.txt
Documentation/aoe/autoload.sh
Documentation/blockdev/floppy.txt
Documentation/cpuidle/sysfs.txt
Documentation/devicetree/bindings/mtd/fsmc-nand.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mtd/spear_smi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power_supply/max17042_battery.txt [new file with mode: 0644]
Documentation/dontdiff
Documentation/fb/intel810.txt
Documentation/fb/intelfb.txt
Documentation/feature-removal-schedule.txt
Documentation/i2c/busses/scx200_acb
Documentation/ide/ide.txt
Documentation/input/input.txt
Documentation/isdn/README.gigaset
Documentation/kbuild/kconfig.txt
Documentation/laptops/sonypi.txt
Documentation/mono.txt
Documentation/networking/baycom.txt
Documentation/networking/bonding.txt
Documentation/networking/dl2k.txt
Documentation/networking/e100.txt
Documentation/networking/ipv6.txt
Documentation/networking/ixgb.txt
Documentation/networking/ltpc.txt
Documentation/networking/vortex.txt
Documentation/parport.txt
Documentation/s390/3270.txt
Documentation/scsi/00-INDEX
Documentation/scsi/aic79xx.txt
Documentation/scsi/aic7xxx.txt
Documentation/scsi/osst.txt
Documentation/scsi/st.txt
Documentation/scsi/ufs.txt [new file with mode: 0644]
Documentation/serial/computone.txt
Documentation/serial/rocket.txt
Documentation/serial/stallion.txt
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/Audiophile-Usb.txt
Documentation/sound/alsa/MIXART.txt
Documentation/sound/alsa/OSS-Emulation.txt
Documentation/sound/oss/AudioExcelDSP16
Documentation/sound/oss/CMI8330
Documentation/sound/oss/Introduction
Documentation/sound/oss/Opti
Documentation/sound/oss/PAS16
Documentation/sound/oss/README.modules
Documentation/sysrq.txt
Documentation/usb/power-management.txt
Documentation/video4linux/CQcam.txt
Documentation/video4linux/Zoran
Documentation/video4linux/bttv/Modules.conf
Documentation/video4linux/meye.txt
MAINTAINERS
Makefile
arch/arm/boot/Makefile
arch/arm/include/asm/cpuidle.h [new file with mode: 0644]
arch/arm/kernel/Makefile
arch/arm/kernel/cpuidle.c [new file with mode: 0644]
arch/arm/mach-at91/at91x40.c
arch/arm/mach-at91/cpuidle.c
arch/arm/mach-at91/setup.c
arch/arm/mach-clps711x/edb7211-mm.c
arch/arm/mach-davinci/cpuidle.c
arch/arm/mach-h720x/common.c
arch/arm/mach-imx/mm-imx3.c
arch/arm/mach-imx/mm-imx5.c
arch/arm/mach-ixp23xx/core.c
arch/arm/mach-ixp4xx/common.c
arch/arm/mach-kirkwood/cpuidle.c
arch/arm/mach-lpc32xx/clock.c
arch/arm/mach-omap1/board-h2.c
arch/arm/mach-omap1/board-h3.c
arch/arm/mach-omap1/board-htcherald.c
arch/arm/mach-omap1/board-innovator.c
arch/arm/mach-omap1/board-nokia770.c
arch/arm/mach-omap1/board-osk.c
arch/arm/mach-omap1/board-palmte.c
arch/arm/mach-omap1/board-palmtt.c
arch/arm/mach-omap1/board-palmz71.c
arch/arm/mach-omap1/board-voiceblue.c
arch/arm/mach-omap1/flash.c
arch/arm/mach-omap1/pm.c
arch/arm/mach-omap2/board-2430sdp.c
arch/arm/mach-omap2/board-4430sdp.c
arch/arm/mach-omap2/board-apollon.c
arch/arm/mach-omap2/board-devkit8000.c
arch/arm/mach-omap2/board-h4.c
arch/arm/mach-omap2/board-omap3evm.c
arch/arm/mach-omap2/board-omap4panda.c
arch/arm/mach-omap2/board-rx51-peripherals.c
arch/arm/mach-omap2/board-zoom-debugboard.c
arch/arm/mach-omap2/board-zoom-peripherals.c
arch/arm/mach-omap2/common-board-devices.c
arch/arm/mach-omap2/cpuidle34xx.c
arch/arm/mach-omap2/cpuidle44xx.c
arch/arm/mach-omap2/display.c
arch/arm/mach-omap2/pm.c
arch/arm/mach-pxa/cm-x300.c
arch/arm/mach-pxa/em-x270.c
arch/arm/mach-pxa/hx4700.c
arch/arm/mach-pxa/magician.c
arch/arm/mach-pxa/raumfeld.c
arch/arm/mach-pxa/stargate2.c
arch/arm/mach-s3c24xx/simtec-nor.c
arch/arm/mach-shark/core.c
arch/arm/mach-shmobile/board-mackerel.c
arch/arm/mach-shmobile/clock-sh7372.c
arch/arm/mach-shmobile/cpuidle.c
arch/arm/mach-tegra/board-dt-tegra20.c
arch/arm/mach-tegra/devices.c
arch/arm/mach-tegra/devices.h
arch/arm/mach-u300/core.c
arch/arm/mach-u300/include/mach/u300-regs.h
arch/arm/mach-w90x900/dev.c
arch/arm/plat-omap/include/plat/gpio.h
arch/arm/plat-s3c24xx/cpu.c
arch/arm/plat-spear/include/plat/keyboard.h
arch/avr32/boot/images/Makefile
arch/blackfin/boot/Makefile
arch/ia64/include/asm/cmpxchg.h
arch/microblaze/boot/Makefile
arch/mips/cavium-octeon/flash_setup.c
arch/parisc/include/asm/futex.h
arch/parisc/kernel/smp.c
arch/s390/include/asm/cpu_mf.h
arch/s390/include/asm/mmu.h
arch/s390/kernel/lgr.c
arch/s390/kernel/perf_cpum_cf.c
arch/s390/kernel/perf_event.c
arch/s390/kernel/setup.c
arch/s390/kernel/smp.c
arch/sh/Kconfig
arch/sh/boards/mach-ecovec24/setup.c
arch/sh/boot/Makefile
arch/sh/drivers/dma/dma-g2.c
arch/sh/drivers/dma/dmabrg.c
arch/sh/drivers/pci/pci-sh7780.c
arch/sh/include/asm/io.h
arch/sh/include/asm/unistd.h
arch/sh/include/asm/unistd_32.h
arch/sh/include/asm/unistd_64.h
arch/sh/include/cpu-sh4/cpu/dma-register.h
arch/sh/include/mach-common/mach/mangle-port.h [new file with mode: 0644]
arch/sh/kernel/cpu/sh4a/setup-sh7757.c
arch/sh/kernel/cpu/shmobile/cpuidle.c
arch/sh/kernel/cpufreq.c
arch/sh/kernel/signal_32.c
arch/sh/kernel/signal_64.c
arch/sh/kernel/syscalls_32.S
arch/sh/kernel/syscalls_64.S
arch/sparc/boot/Makefile
arch/sparc/include/asm/ptrace.h
arch/sparc/kernel/jump_label.c
arch/sparc/kernel/kgdb_64.c
arch/sparc/kernel/sun4d_smp.c
arch/sparc/kernel/sun4m_smp.c
arch/unicore32/boot/Makefile
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event.h
arch/x86/kernel/cpu/perf_event_amd.c
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/cpu/perf_event_p6.c
arch/x86/kernel/smpboot.c
arch/x86/kernel/tboot.c
drivers/acpi/Kconfig
drivers/acpi/Makefile
drivers/acpi/acpica/Makefile
drivers/acpi/acpica/accommon.h
drivers/acpi/acpica/acdebug.h
drivers/acpi/acpica/acevents.h
drivers/acpi/acpica/acglobal.h
drivers/acpi/acpica/achware.h
drivers/acpi/acpica/aclocal.h
drivers/acpi/acpica/acmacros.h
drivers/acpi/acpica/acnamesp.h
drivers/acpi/acpica/actables.h
drivers/acpi/acpica/evevent.c
drivers/acpi/acpica/evglock.c
drivers/acpi/acpica/evgpe.c
drivers/acpi/acpica/evgpeblk.c
drivers/acpi/acpica/evgpeinit.c
drivers/acpi/acpica/evgpeutil.c
drivers/acpi/acpica/evmisc.c
drivers/acpi/acpica/evsci.c
drivers/acpi/acpica/evxface.c
drivers/acpi/acpica/evxfevnt.c
drivers/acpi/acpica/evxfgpe.c
drivers/acpi/acpica/hwacpi.c
drivers/acpi/acpica/hwesleep.c [new file with mode: 0644]
drivers/acpi/acpica/hwgpe.c
drivers/acpi/acpica/hwregs.c
drivers/acpi/acpica/hwsleep.c
drivers/acpi/acpica/hwtimer.c
drivers/acpi/acpica/hwxface.c
drivers/acpi/acpica/hwxfsleep.c [new file with mode: 0644]
drivers/acpi/acpica/nsdump.c
drivers/acpi/acpica/nsdumpdv.c
drivers/acpi/acpica/nspredef.c
drivers/acpi/acpica/nsrepair.c
drivers/acpi/acpica/nsutils.c
drivers/acpi/acpica/tbfadt.c
drivers/acpi/acpica/tbinstal.c
drivers/acpi/acpica/tbutils.c
drivers/acpi/acpica/utdecode.c
drivers/acpi/acpica/utglobal.c
drivers/acpi/acpica/utinit.c
drivers/acpi/acpica/utxface.c
drivers/acpi/apei/apei-base.c
drivers/acpi/apei/cper.c
drivers/acpi/apei/einj.c
drivers/acpi/apei/erst.c
drivers/acpi/bgrt.c [new file with mode: 0644]
drivers/acpi/bus.c
drivers/acpi/ec.c
drivers/acpi/nvs.c
drivers/acpi/osl.c
drivers/acpi/power.c
drivers/acpi/processor_driver.c
drivers/acpi/processor_idle.c
drivers/acpi/processor_thermal.c
drivers/acpi/processor_throttling.c
drivers/acpi/reboot.c
drivers/acpi/scan.c
drivers/acpi/sleep.c
drivers/acpi/thermal.c
drivers/acpi/video.c
drivers/cpuidle/cpuidle.c
drivers/cpuidle/driver.c
drivers/cpuidle/governors/menu.c
drivers/cpuidle/sysfs.c
drivers/dma/mxs-dma.c
drivers/input/joystick/amijoy.c
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/tegra-kbc.c
drivers/input/mouse/sentelic.c
drivers/input/mouse/sentelic.h
drivers/input/serio/ams_delta_serio.c
drivers/input/tablet/Kconfig
drivers/input/tablet/wacom.h
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h
drivers/mmc/host/mxs-mmc.c
drivers/mtd/Kconfig
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/chips/cfi_cmdset_0020.c
drivers/mtd/chips/cfi_util.c
drivers/mtd/chips/fwh_lock.h
drivers/mtd/chips/map_absent.c
drivers/mtd/chips/map_ram.c
drivers/mtd/chips/map_rom.c
drivers/mtd/devices/Kconfig
drivers/mtd/devices/Makefile
drivers/mtd/devices/block2mtd.c
drivers/mtd/devices/doc2000.c
drivers/mtd/devices/doc2001.c
drivers/mtd/devices/doc2001plus.c
drivers/mtd/devices/docg3.c
drivers/mtd/devices/docg3.h
drivers/mtd/devices/lart.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/ms02-nv.c
drivers/mtd/devices/mtd_dataflash.c
drivers/mtd/devices/mtdram.c
drivers/mtd/devices/phram.c
drivers/mtd/devices/pmc551.c
drivers/mtd/devices/slram.c
drivers/mtd/devices/spear_smi.c [new file with mode: 0644]
drivers/mtd/devices/sst25l.c
drivers/mtd/inftlcore.c
drivers/mtd/lpddr/lpddr_cmds.c
drivers/mtd/maps/bfin-async-flash.c
drivers/mtd/maps/dc21285.c
drivers/mtd/maps/gpio-addr-flash.c
drivers/mtd/maps/h720x-flash.c
drivers/mtd/maps/impa7.c
drivers/mtd/maps/intel_vr_nor.c
drivers/mtd/maps/ixp2000.c
drivers/mtd/maps/ixp4xx.c
drivers/mtd/maps/l440gx.c
drivers/mtd/maps/lantiq-flash.c
drivers/mtd/maps/latch-addr-flash.c
drivers/mtd/maps/pcmciamtd.c
drivers/mtd/maps/physmap.c
drivers/mtd/maps/plat-ram.c
drivers/mtd/maps/pxa2xx-flash.c
drivers/mtd/maps/rbtx4939-flash.c
drivers/mtd/maps/sa1100-flash.c
drivers/mtd/maps/solutionengine.c
drivers/mtd/maps/uclinux.c
drivers/mtd/maps/vmu-flash.c
drivers/mtd/maps/wr_sbc82xx_flash.c
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdblock.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdoops.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/alauda.c
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/bcm_umi_nand.c
drivers/mtd/nand/bf5xx_nand.c
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/cmx270_nand.c
drivers/mtd/nand/cs553x_nand.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/denali.c
drivers/mtd/nand/diskonchip.c
drivers/mtd/nand/docg4.c [new file with mode: 0644]
drivers/mtd/nand/fsl_elbc_nand.c
drivers/mtd/nand/fsmc_nand.c
drivers/mtd/nand/gpmi-nand/gpmi-lib.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.h
drivers/mtd/nand/h1910.c
drivers/mtd/nand/jz4740_nand.c
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/ndfc.c
drivers/mtd/nand/omap2.c
drivers/mtd/nand/orion_nand.c
drivers/mtd/nand/plat_nand.c
drivers/mtd/nand/ppchameleonevb.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/mtd/nand/r852.c
drivers/mtd/nand/rtc_from4.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/nand/sh_flctl.c
drivers/mtd/nand/sharpsl.c
drivers/mtd/nand/tmio_nand.c
drivers/mtd/nand/txx9ndfmc.c
drivers/mtd/nftlcore.c
drivers/mtd/onenand/generic.c
drivers/mtd/onenand/omap2.c
drivers/mtd/onenand/onenand_base.c
drivers/mtd/onenand/samsung.c
drivers/mtd/redboot.c
drivers/mtd/sm_ftl.c
drivers/mtd/ubi/gluebi.c
drivers/net/ethernet/sfc/mtd.c
drivers/net/wan/Kconfig
drivers/pci/pci-acpi.c
drivers/pci/pcie/aspm.c
drivers/platform/x86/intel_ips.c
drivers/pnp/pnpacpi/core.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/ab8500_btemp.c [new file with mode: 0644]
drivers/power/ab8500_charger.c [new file with mode: 0644]
drivers/power/ab8500_fg.c [new file with mode: 0644]
drivers/power/abx500_chargalg.c [new file with mode: 0644]
drivers/power/charger-manager.c
drivers/power/da9052-battery.c
drivers/power/ds2782_battery.c
drivers/power/isp1704_charger.c
drivers/power/lp8727_charger.c
drivers/power/max17040_battery.c
drivers/power/max17042_battery.c
drivers/power/sbs-battery.c
drivers/power/smb347-charger.c [new file with mode: 0644]
drivers/power/z2_battery.c
drivers/scsi/Kconfig
drivers/scsi/Makefile
drivers/scsi/aic7xxx/aic79xx_osm.c
drivers/scsi/aic7xxx/aic7xxx_osm.c
drivers/scsi/atp870u.c
drivers/scsi/bfa/bfa.h
drivers/scsi/bfa/bfa_core.c
drivers/scsi/bfa/bfa_defs_svc.h
drivers/scsi/bfa/bfa_fcs_lport.c
drivers/scsi/bfa/bfa_fcs_rport.c
drivers/scsi/bfa/bfa_ioc.c
drivers/scsi/bfa/bfa_ioc.h
drivers/scsi/bfa/bfa_ioc_ct.c
drivers/scsi/bfa/bfa_svc.c
drivers/scsi/bfa/bfa_svc.h
drivers/scsi/bfa/bfad_attr.c
drivers/scsi/bfa/bfad_bsg.c
drivers/scsi/bfa/bfad_bsg.h
drivers/scsi/bfa/bfad_drv.h
drivers/scsi/bfa/bfi_ms.h
drivers/scsi/bfa/bfi_reg.h
drivers/scsi/bnx2fc/bnx2fc_fcoe.c
drivers/scsi/fcoe/fcoe.c
drivers/scsi/fcoe/fcoe_ctlr.c
drivers/scsi/ipr.c
drivers/scsi/ipr.h
drivers/scsi/libfc/fc_exch.c
drivers/scsi/libfc/fc_lport.c
drivers/scsi/lpfc/Makefile
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_attr.c
drivers/scsi/lpfc/lpfc_debugfs.c
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_hw4.h
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_nportdisc.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_scsi.h
drivers/scsi/lpfc/lpfc_sli.c
drivers/scsi/lpfc/lpfc_version.h
drivers/scsi/mpt2sas/mpt2sas_base.c
drivers/scsi/mpt2sas/mpt2sas_ctl.c
drivers/scsi/pm8001/pm8001_hwi.c
drivers/scsi/qla4xxx/ql4_isr.c
drivers/scsi/qla4xxx/ql4_os.c
drivers/scsi/qla4xxx/ql4_version.h
drivers/scsi/scsi_debug.c
drivers/scsi/scsi_transport_iscsi.c
drivers/scsi/sd.c
drivers/scsi/st.c
drivers/scsi/st.h
drivers/scsi/ufs/Kconfig [new file with mode: 0644]
drivers/scsi/ufs/Makefile [new file with mode: 0644]
drivers/scsi/ufs/ufs.h [new file with mode: 0644]
drivers/scsi/ufs/ufshcd.c [new file with mode: 0644]
drivers/scsi/ufs/ufshci.h [new file with mode: 0644]
drivers/scsi/vmw_pvscsi.c
drivers/scsi/vmw_pvscsi.h
drivers/sh/intc/chip.c
drivers/sh/intc/core.c
drivers/sh/intc/handle.c
drivers/sh/intc/internals.h
drivers/staging/asus_oled/README
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/spear_thermal.c [new file with mode: 0644]
drivers/thermal/thermal_sys.c
drivers/tty/isicom.c
drivers/tty/serial/sh-sci.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/storage/Kconfig
fs/btrfs/async-thread.c
fs/btrfs/async-thread.h
fs/btrfs/backref.c
fs/btrfs/backref.h
fs/btrfs/compression.c
fs/btrfs/compression.h
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/delayed-inode.c
fs/btrfs/delayed-ref.c
fs/btrfs/dir-item.c
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/export.c
fs/btrfs/extent-tree.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/file-item.c
fs/btrfs/file.c
fs/btrfs/free-space-cache.c
fs/btrfs/inode-item.c
fs/btrfs/inode-map.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/locking.c
fs/btrfs/locking.h
fs/btrfs/ordered-data.c
fs/btrfs/ordered-data.h
fs/btrfs/orphan.c
fs/btrfs/reada.c
fs/btrfs/relocation.c
fs/btrfs/root-tree.c
fs/btrfs/scrub.c
fs/btrfs/struct-funcs.c
fs/btrfs/super.c
fs/btrfs/transaction.c
fs/btrfs/transaction.h
fs/btrfs/tree-log.c
fs/btrfs/tree-log.h
fs/btrfs/volumes.c
fs/btrfs/volumes.h
fs/cifs/cifs_debug.c
fs/cifs/cifs_debug.h
fs/cifs/cifsfs.c
fs/cifs/cifsfs.h
fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/file.c
fs/cifs/misc.c
fs/cifs/netmisc.c
fs/cifs/transport.c
fs/jffs2/acl.c
fs/jffs2/background.c
fs/jffs2/build.c
fs/jffs2/compr.c
fs/jffs2/compr_lzo.c
fs/jffs2/compr_rubin.c
fs/jffs2/compr_zlib.c
fs/jffs2/debug.c
fs/jffs2/debug.h
fs/jffs2/dir.c
fs/jffs2/erase.c
fs/jffs2/file.c
fs/jffs2/fs.c
fs/jffs2/gc.c
fs/jffs2/malloc.c
fs/jffs2/nodelist.c
fs/jffs2/nodemgmt.c
fs/jffs2/os-linux.h
fs/jffs2/read.c
fs/jffs2/readinode.c
fs/jffs2/scan.c
fs/jffs2/security.c
fs/jffs2/summary.c
fs/jffs2/super.c
fs/jffs2/symlink.c
fs/jffs2/wbuf.c
fs/jffs2/write.c
fs/jffs2/xattr.c
fs/romfs/storage.c
include/acpi/acconfig.h [moved from drivers/acpi/acpica/acconfig.h with 92% similarity]
include/acpi/acexcep.h
include/acpi/acnames.h
include/acpi/acpi_bus.h
include/acpi/acpiosxf.h
include/acpi/acpixf.h
include/acpi/actbl.h
include/acpi/actypes.h
include/linux/acpi.h
include/linux/cpuidle.h
include/linux/fsl/mxs-dma.h [moved from arch/arm/mach-mxs/include/mach/dma.h with 100% similarity]
include/linux/ftrace_event.h
include/linux/gpio_keys.h
include/linux/kernel.h
include/linux/lp8727.h
include/linux/mfd/abx500.h
include/linux/mfd/abx500/ab8500-bm.h [new file with mode: 0644]
include/linux/mfd/abx500/ux500_chargalg.h [new file with mode: 0644]
include/linux/mtd/bbm.h
include/linux/mtd/blktrans.h
include/linux/mtd/fsmc.h
include/linux/mtd/mtd.h
include/linux/mtd/nand.h
include/linux/mtd/pmc551.h [deleted file]
include/linux/mtd/sh_flctl.h
include/linux/mtd/spear_smi.h [new file with mode: 0644]
include/linux/mtio.h
include/linux/perf_event.h
include/linux/platform_data/spear_thermal.h [new file with mode: 0644]
include/linux/power/max17042_battery.h
include/linux/power/smb347-charger.h [new file with mode: 0644]
include/linux/ring_buffer.h
include/linux/tboot.h
include/scsi/iscsi_if.h
include/scsi/libfcoe.h
include/trace/events/btrfs.h
kernel/cgroup.c
kernel/events/core.c
kernel/futex.c
kernel/futex_compat.c
kernel/irq/handle.c
kernel/irq/manage.c
kernel/irq/migration.c
kernel/sched/core.c
kernel/sched/sched.h
kernel/trace/Kconfig
kernel/trace/ftrace.c
kernel/trace/ring_buffer.c
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_entries.h
kernel/trace/trace_export.c
scripts/Kbuild.include
scripts/Makefile.build
scripts/Makefile.lib
scripts/coccinelle/api/ptr_ret.cocci [new file with mode: 0644]
scripts/coccinelle/free/clk_put.cocci [new file with mode: 0644]
scripts/coccinelle/free/iounmap.cocci [new file with mode: 0644]
scripts/coccinelle/misc/boolinit.cocci [new file with mode: 0644]
scripts/coccinelle/misc/cstptr.cocci [new file with mode: 0644]
scripts/coccinelle/null/badzero.cocci [new file with mode: 0644]
scripts/dtc/dtc.c
scripts/dtc/flattree.c
scripts/headers_check.pl
scripts/kconfig/confdata.c
scripts/kconfig/merge_config.sh [changed mode: 0644->0755]
scripts/kconfig/symbol.c
scripts/mod/modpost.c
scripts/package/builddeb
scripts/patch-kernel
scripts/setlocalversion
scripts/tags.sh
security/selinux/avc.c
sound/core/seq/seq_dummy.c
sound/drivers/Kconfig
sound/isa/opti9xx/opti92x-ad1848.c
sound/oss/msnd_pinnacle.c
sound/pci/asihpi/hpi_internal.h
sound/pci/asihpi/hpios.c
sound/pci/hda/patch_realtek.c
sound/soc/codecs/wm8994.c
sound/soc/mxs/mxs-pcm.c
sound/soc/mxs/mxs-saif.c
sound/soc/soc-dmaengine-pcm.c
tools/perf/Documentation/perf-report.txt
tools/perf/Makefile
tools/perf/builtin-diff.c
tools/perf/builtin-report.c
tools/perf/builtin-stat.c
tools/perf/builtin-test.c
tools/perf/config/feature-tests.mak
tools/perf/util/annotate.c
tools/perf/util/cache.h
tools/perf/util/evlist.c
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/gtk/browser.c [new file with mode: 0644]
tools/perf/util/gtk/gtk.h [new file with mode: 0644]
tools/perf/util/header.c
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/include/linux/export.h [moved from tools/perf/util/include/linux/module.h with 100% similarity]
tools/perf/util/parse-events.c
tools/perf/util/parse-events.h
tools/perf/util/parse-events.l [new file with mode: 0644]
tools/perf/util/parse-events.y [new file with mode: 0644]
tools/perf/util/pmu.c [new file with mode: 0644]
tools/perf/util/pmu.h [new file with mode: 0644]
tools/perf/util/pmu.l [new file with mode: 0644]
tools/perf/util/pmu.y [new file with mode: 0644]
tools/perf/util/probe-finder.c
tools/perf/util/session.c
tools/perf/util/symbol.c
tools/perf/util/trace-event-parse.c
tools/perf/util/ui/browser.h
tools/perf/util/ui/browsers/hists.c
tools/perf/util/ui/keysyms.h
tools/perf/util/ui/util.c
tools/power/x86/turbostat/turbostat.8
tools/power/x86/turbostat/turbostat.c

index 23a43b8207e6b1519d43f4808fe74b009f107f59..2a7f9a00cb0a6a0acef8fc46bbb6f290f4d0dc67 100644 (file)
@@ -55,7 +55,7 @@ What:         /sys/bus/usb/drivers/usbtmc/devices/*/auto_abort
 Date:          August 2008
 Contact:       Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 Description:
-               This file determines if the the transaction of the USB TMC
+               This file determines if the transaction of the USB TMC
                device is to be automatically aborted if there is any error.
                For more details about this, please see the document,
                "Universal Serial Bus Test and Measurement Class Specification
diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-format b/Documentation/ABI/testing/sysfs-bus-event_source-devices-format
new file mode 100644 (file)
index 0000000..079afc7
--- /dev/null
@@ -0,0 +1,14 @@
+Where:         /sys/bus/event_source/devices/<dev>/format
+Date:          January 2012
+Kernel Version: 3.3
+Contact:       Jiri Olsa <jolsa@redhat.com>
+Description:
+               Attribute group to describe the magic bits that go into
+               perf_event_attr::config[012] for a particular pmu.
+               Each attribute of this group defines the 'hardware' bitmask
+               we want to export, so that userspace can deal with sane
+               name/value pairs.
+
+               Example: 'config1:1,6-10,44'
+               Defines contents of attribute that occupies bits 1,6-10,44 of
+               perf_event_attr::config1.
index 4f9ba3c2fca754c97651172e7222742a08096e9f..dd930c8db41f1c921733cfc5c18ee7b6a91cb868 100644 (file)
@@ -1,3 +1,23 @@
+What:          /sys/firmware/acpi/bgrt/
+Date:          January 2012
+Contact:       Matthew Garrett <mjg@redhat.com>
+Description:
+               The BGRT is an ACPI 5.0 feature that allows the OS
+               to obtain a copy of the firmware boot splash and
+               some associated metadata. This is intended to be used
+               by boot splash applications in order to interact with
+               the firmware boot splash in order to avoid jarring
+               transitions.
+
+               image: The image bitmap. Currently a 32-bit BMP.
+               status: 1 if the image is valid, 0 if firmware invalidated it.
+               type: 0 indicates image is in BMP format.
+               version: The version of the BGRT. Currently 1.
+               xoffset: The number of pixels between the left of the screen
+                        and the left edge of the image.
+               yoffset: The number of pixels between the top of the screen
+                        and the top edge of the image.
+
 What:          /sys/firmware/acpi/interrupts/
 Date:          February 2008
 Contact:       Len Brown <lenb@kernel.org>
index 2b90d328b3ba5595c4f3a0e6bdc7c236346f550c..c58b236bbe0467938e601e498008d4856bbbce52 100644 (file)
@@ -793,6 +793,35 @@ own custom mode, or may have some other magic method for making indentation
 work correctly.
 
 
+               Chapter 19:  Inline assembly
+
+In architecture-specific code, you may need to use inline assembly to interface
+with CPU or platform functionality.  Don't hesitate to do so when necessary.
+However, don't use inline assembly gratuitously when C can do the job.  You can
+and should poke hardware from C when possible.
+
+Consider writing simple helper functions that wrap common bits of inline
+assembly, rather than repeatedly writing them with slight variations.  Remember
+that inline assembly can use C parameters.
+
+Large, non-trivial assembly functions should go in .S files, with corresponding
+C prototypes defined in C header files.  The C prototypes for assembly
+functions should use "asmlinkage".
+
+You may need to mark your asm statement as volatile, to prevent GCC from
+removing it if GCC doesn't notice any side effects.  You don't always need to
+do so, though, and doing so unnecessarily can limit optimization.
+
+When writing a single inline assembly statement containing multiple
+instructions, put each instruction on a separate line in a separate quoted
+string, and end each string except the last with \n\t to properly indent the
+next instruction in the assembly output:
+
+       asm ("magic %reg1, #42\n\t"
+            "more_magic %reg2, %reg3"
+            : /* outputs */ : /* inputs */ : /* clobbers */);
+
+
 
                Appendix I: References
 
index e7cc363972173ca0177de6bfb7ce3f38de89bde2..e20b6daaced48d9f2ab39ef3749792fc34123003 100644 (file)
@@ -53,6 +53,14 @@ directory apei/einj. The following files are provided.
   This file is used to set the second error parameter value. Effect of
   parameter depends on error_type specified.
 
+- notrigger
+  The EINJ mechanism is a two step process. First inject the error, then
+  perform some actions to trigger it. Setting "notrigger" to 1 skips the
+  trigger phase, which *may* allow the user to cause the error in some other
+  context by a simple access to the cpu, memory location, or device that is
+  the target of the error injection. Whether this actually works depends
+  on what operations the BIOS actually includes in the trigger phase.
+
 BIOS versions based in the ACPI 4.0 specification have limited options
 to control where the errors are injected.  Your BIOS may support an
 extension (enabled with the param_extension=1 module parameter, or
index b5aada9f20ccd78a6f09d2aa49338227b2e963b3..5f5aa16047ff4f8f5a9680101572c8afc34e799c 100644 (file)
@@ -35,7 +35,7 @@ CREATING DEVICE NODES
     sh Documentation/aoe/mkshelf.sh /dev/etherd 0
 
   There is also an autoload script that shows how to edit
-  /etc/modprobe.conf to ensure that the aoe module is loaded when
+  /etc/modprobe.d/aoe.conf to ensure that the aoe module is loaded when
   necessary.
 
 USING DEVICE NODES
index 78dad1334c6fc90d70f879df13a200e851f887ac..815dff4691c946d38c0f472d072354a0cfe1ae44 100644 (file)
@@ -1,8 +1,8 @@
 #!/bin/sh
 # set aoe to autoload by installing the
-# aliases in /etc/modprobe.conf
+# aliases in /etc/modprobe.d/
 
-f=/etc/modprobe.conf
+f=/etc/modprobe.d/aoe.conf
 
 if test ! -r $f || test ! -w $f; then
        echo "cannot configure $f for module autoloading" 1>&2
index 6ccab88705cbae4d327c143b7608363ebc2459e0..470fe4b5e37989d5b1b623b25519bd44152fb62d 100644 (file)
@@ -49,7 +49,7 @@ you can put:
 
  options floppy omnibook messages
 
-in /etc/modprobe.conf.
+in a configuration file in /etc/modprobe.d/.
 
 
  The floppy driver related options are:
index 50d7b1642759365f63ea2ddf46e8d6ef1c7b6ab1..9d28a3406e745589383021732fe7941ed9aad1cb 100644 (file)
@@ -36,6 +36,7 @@ drwxr-xr-x 2 root root 0 Feb  8 10:42 state3
 /sys/devices/system/cpu/cpu0/cpuidle/state0:
 total 0
 -r--r--r-- 1 root root 4096 Feb  8 10:42 desc
+-rw-r--r-- 1 root root 4096 Feb  8 10:42 disable
 -r--r--r-- 1 root root 4096 Feb  8 10:42 latency
 -r--r--r-- 1 root root 4096 Feb  8 10:42 name
 -r--r--r-- 1 root root 4096 Feb  8 10:42 power
@@ -45,6 +46,7 @@ total 0
 /sys/devices/system/cpu/cpu0/cpuidle/state1:
 total 0
 -r--r--r-- 1 root root 4096 Feb  8 10:42 desc
+-rw-r--r-- 1 root root 4096 Feb  8 10:42 disable
 -r--r--r-- 1 root root 4096 Feb  8 10:42 latency
 -r--r--r-- 1 root root 4096 Feb  8 10:42 name
 -r--r--r-- 1 root root 4096 Feb  8 10:42 power
@@ -54,6 +56,7 @@ total 0
 /sys/devices/system/cpu/cpu0/cpuidle/state2:
 total 0
 -r--r--r-- 1 root root 4096 Feb  8 10:42 desc
+-rw-r--r-- 1 root root 4096 Feb  8 10:42 disable
 -r--r--r-- 1 root root 4096 Feb  8 10:42 latency
 -r--r--r-- 1 root root 4096 Feb  8 10:42 name
 -r--r--r-- 1 root root 4096 Feb  8 10:42 power
@@ -63,6 +66,7 @@ total 0
 /sys/devices/system/cpu/cpu0/cpuidle/state3:
 total 0
 -r--r--r-- 1 root root 4096 Feb  8 10:42 desc
+-rw-r--r-- 1 root root 4096 Feb  8 10:42 disable
 -r--r--r-- 1 root root 4096 Feb  8 10:42 latency
 -r--r--r-- 1 root root 4096 Feb  8 10:42 name
 -r--r--r-- 1 root root 4096 Feb  8 10:42 power
@@ -72,6 +76,7 @@ total 0
 
 
 * desc : Small description about the idle state (string)
+* disable : Option to disable this idle state (bool)
 * latency : Latency to exit out of this idle state (in microseconds)
 * name : Name of the idle state (string)
 * power : Power consumed while in this idle state (in milliwatts)
diff --git a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt
new file mode 100644 (file)
index 0000000..e2c663b
--- /dev/null
@@ -0,0 +1,33 @@
+* FSMC NAND
+
+Required properties:
+- compatible : "st,spear600-fsmc-nand"
+- reg : Address range of the mtd chip
+- reg-names: Should contain the reg names "fsmc_regs" and "nand_data"
+- st,ale-off : Chip specific offset to ALE
+- st,cle-off : Chip specific offset to CLE
+
+Optional properties:
+- bank-width : Width (in bytes) of the device.  If not present, the width
+  defaults to 1 byte
+- nand-skip-bbtscan: Indicates the the BBT scanning should be skipped
+
+Example:
+
+       fsmc: flash@d1800000 {
+               compatible = "st,spear600-fsmc-nand";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               reg = <0xd1800000 0x1000        /* FSMC Register */
+                      0xd2000000 0x4000>;      /* NAND Base */
+               reg-names = "fsmc_regs", "nand_data";
+               st,ale-off = <0x20000>;
+               st,cle-off = <0x10000>;
+
+               bank-width = <1>;
+               nand-skip-bbtscan;
+
+               partition@0 {
+                       ...
+               };
+       };
diff --git a/Documentation/devicetree/bindings/mtd/spear_smi.txt b/Documentation/devicetree/bindings/mtd/spear_smi.txt
new file mode 100644 (file)
index 0000000..7248aad
--- /dev/null
@@ -0,0 +1,31 @@
+* SPEAr SMI
+
+Required properties:
+- compatible : "st,spear600-smi"
+- reg : Address range of the mtd chip
+- #address-cells, #size-cells : Must be present if the device has sub-nodes
+  representing partitions.
+- interrupt-parent: Should be the phandle for the interrupt controller
+  that services interrupts for this device
+- interrupts: Should contain the STMMAC interrupts
+- clock-rate : Functional clock rate of SMI in Hz
+
+Optional properties:
+- st,smi-fast-mode : Flash supports read in fast mode
+
+Example:
+
+       smi: flash@fc000000 {
+               compatible = "st,spear600-smi";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               reg = <0xfc000000 0x1000>;
+               interrupt-parent = <&vic1>;
+               interrupts = <12>;
+               clock-rate = <50000000>;        /* 50MHz */
+
+               flash@f8000000 {
+                       st,smi-fast-mode;
+                       ...
+               };
+       };
diff --git a/Documentation/devicetree/bindings/power_supply/max17042_battery.txt b/Documentation/devicetree/bindings/power_supply/max17042_battery.txt
new file mode 100644 (file)
index 0000000..5bc9b68
--- /dev/null
@@ -0,0 +1,18 @@
+max17042_battery
+~~~~~~~~~~~~~~~~
+
+Required properties :
+ - compatible : "maxim,max17042"
+
+Optional properties :
+ - maxim,rsns-microohm : Resistance of rsns resistor in micro Ohms
+                         (datasheet-recommended value is 10000).
+   Defining this property enables current-sense functionality.
+
+Example:
+
+       battery-charger@36 {
+               compatible = "maxim,max17042";
+               reg = <0x36>;
+               maxim,rsns-microohm = <10000>;
+       };
index 0c083c5c2faaa6526872015a7abc60411b191ba2..b4a898f43c37e72d728c17013520e44f56c87a5d 100644 (file)
@@ -158,7 +158,6 @@ logo_*.c
 logo_*_clut224.c
 logo_*_mono.c
 lxdialog
-mach
 mach-types
 mach-types.h
 machtypes.h
index be3e7836abef235c5140cf7810eee84f27b8d835..a8e9f5bca6f37aa0a11ee07ab70a2c5cfe4d7d2b 100644 (file)
@@ -211,7 +211,7 @@ Using the same setup as described above, load the module like this:
        modprobe i810fb vram=2 xres=1024 bpp=8 hsync1=30 hsync2=55 vsync1=50 \
                 vsync2=85 accel=1 mtrr=1
 
-Or just add the following to /etc/modprobe.conf
+Or just add the following to a configuration file in /etc/modprobe.d/
 
        options i810fb vram=2 xres=1024 bpp=16 hsync1=30 hsync2=55 vsync1=50 \
        vsync2=85 accel=1 mtrr=1
index dd9e944ea6283c7e7e1f321458f94affedfdbdd2..feac4e4d69688067c97d69767e41d290b1dc682b 100644 (file)
@@ -120,7 +120,7 @@ Using the same setup as described above, load the module like this:
 
        modprobe intelfb mode=800x600-32@75 vram=8 accel=1 hwcursor=1
 
-Or just add the following to /etc/modprobe.conf
+Or just add the following to a configuration file in /etc/modprobe.d/
 
        options intelfb mode=800x600-32@75 vram=8 accel=1 hwcursor=1
 
index 0cad4803ffacd2c021f33407f21e9ead735c9bac..c1be8066ea5914c33e9a7aacbfee66f8455741b2 100644 (file)
@@ -529,3 +529,13 @@ When:      3.5
 Why:   The old kmap_atomic() with two arguments is deprecated, we only
        keep it for backward compatibility for few cycles and then drop it.
 Who:   Cong Wang <amwang@redhat.com>
+
+----------------------------
+
+What:  get_robust_list syscall
+When:  2013
+Why:   There appear to be no production users of the get_robust_list syscall,
+       and it runs the risk of leaking address locations, allowing the bypass
+       of ASLR. It was only ever intended for debugging, so it should be
+       removed.
+Who:   Kees Cook <keescook@chromium.org>
index 7c07883d4dfc054302561791cbef9366110d6edc..ce83c871fe95046b5f1731189a6640cacbc0645e 100644 (file)
@@ -28,5 +28,5 @@ If the scx200_acb driver is built into the kernel, add the following
 parameter to your boot command line:
   scx200_acb.base=0x810,0x820
 If the scx200_acb driver is built as a module, add the following line to
-the file /etc/modprobe.conf instead:
+a configuration file in /etc/modprobe.d/ instead:
   options scx200_acb base=0x810,0x820
index e77bebfa7b0d9b7cd459050970581034281538ee..7aca987c23d9f97d4932d8d6081af26a2df32d38 100644 (file)
@@ -169,7 +169,7 @@ When using ide.c as a module in combination with kmod, add:
 
        alias block-major-3 ide-probe
 
-to /etc/modprobe.conf.
+to a configuration file in /etc/modprobe.d/.
 
 When ide.c is used as a module, you can pass command line parameters to the
 driver using the "options=" keyword to insmod, while replacing any ',' with
index b3d6787b4fb11b0c55c562ef1c8f80d1e6f13668..666c06c5ab0c228701e5f32875376b0119cf41d1 100644 (file)
@@ -250,8 +250,8 @@ And so on up to event31.
 a USB keyboard works and is correctly connected to the kernel keyboard
 driver. 
 
-  Doing a cat /dev/input/mouse0 (c, 13, 32) will verify that a mouse
-is also emulated, characters should appear if you move it.
+  Doing a "cat /dev/input/mouse0" (c, 13, 32) will verify that a mouse
+is also emulated; characters should appear if you move it.
 
   You can test the joystick emulation with the 'jstest' utility,
 available in the joystick package (see Documentation/input/joystick.txt).
index ef3343eaa00246c83493f384ddfee46514471504..7534c6039adc824e63d6992455187945d61cf115 100644 (file)
@@ -97,8 +97,7 @@ GigaSet 307x Device Driver
                                   2.5.): 1=on (default), 0=off
 
      Depending on your distribution you may want to create a separate module
-     configuration file /etc/modprobe.d/gigaset for these, or add them to a
-     custom file like /etc/modprobe.conf.local.
+     configuration file like /etc/modprobe.d/gigaset.conf for these.
 
 2.2. Device nodes for user space programs
      ------------------------------------
@@ -212,8 +211,8 @@ GigaSet 307x Device Driver
 
         options ppp_async flag_time=0
 
-     to an appropriate module configuration file, like /etc/modprobe.d/gigaset
-     or /etc/modprobe.conf.local.
+     to an appropriate module configuration file, like
+     /etc/modprobe.d/gigaset.conf.
 
      Unimodem mode is needed for making some devices [e.g. SX100] work which
      do not support the regular Gigaset command set. If debug output (see
@@ -237,8 +236,8 @@ GigaSet 307x Device Driver
        modprobe usb_gigaset startmode=0
      or by adding a line like
        options usb_gigaset startmode=0
-     to an appropriate module configuration file, like /etc/modprobe.d/gigaset
-     or /etc/modprobe.conf.local.
+     to an appropriate module configuration file, like
+     /etc/modprobe.d/gigaset.conf
 
 2.6. Call-ID (CID) mode
      ------------------
@@ -310,7 +309,7 @@ GigaSet 307x Device Driver
 
            options isdn dialtimeout=15
 
-        to /etc/modprobe.d/gigaset, /etc/modprobe.conf.local or a similar file.
+        to /etc/modprobe.d/gigaset.conf or a similar file.
 
      Problem:
         The isdnlog program emits error messages or just doesn't work.
@@ -350,8 +349,7 @@ GigaSet 307x Device Driver
      The initial value can be set using the debug parameter when loading the
      module "gigaset", e.g. by adding a line
         options gigaset debug=0
-     to your module configuration file, eg. /etc/modprobe.d/gigaset or
-     /etc/modprobe.conf.local.
+     to your module configuration file, eg. /etc/modprobe.d/gigaset.conf
 
      Generated debugging information can be found
      - as output of the command
index c313d71324b4a83db6934114e86af7eed0f30be6..9d5f2a90dca96b600c1ee1bd107620c6dd1e4523 100644 (file)
@@ -28,12 +28,10 @@ new (default) values, so you can use:
 
        grep "(NEW)" conf.new
 
-to see the new config symbols or you can 'diff' the previous and
-new .config files to see the differences:
+to see the new config symbols or you can use diffconfig to see the
+differences between the previous and new .config files:
 
-       diff .config.old .config | less
-
-(Yes, we need something better here.)
+       scripts/diffconfig .config.old .config | less
 
 ______________________________________________________________________
 Environment variables for '*config'
index 4857acfc50f1fa025f71b7d49cd0066381642750..606bdb9ce036391cbd6a605c609ca4683d41a1c0 100644 (file)
@@ -110,7 +110,7 @@ Module use:
 -----------
 
 In order to automatically load the sonypi module on use, you can put those
-lines in your /etc/modprobe.conf file:
+lines a configuration file in /etc/modprobe.d/:
 
        alias char-major-10-250 sonypi
        options sonypi minor=250
index e8e1758e87dab7244d1b92a2c4400764e53bb687..d01ac60521943756a99bfc07fe8fe05e6775626f 100644 (file)
@@ -38,11 +38,11 @@ if [ ! -e /proc/sys/fs/binfmt_misc/register ]; then
         /sbin/modprobe binfmt_misc
        # Some distributions, like Fedora Core, perform
        # the following command automatically when the
-       # binfmt_misc module is loaded into the kernel.
+       # binfmt_misc module is loaded into the kernel
+       # or during normal boot up (systemd-based systems).
        # Thus, it is possible that the following line
-       # is not needed at all. Look at /etc/modprobe.conf
-       # to check whether this is applicable or not.
-        mount -t binfmt_misc none /proc/sys/fs/binfmt_misc
+       # is not needed at all.
+       mount -t binfmt_misc none /proc/sys/fs/binfmt_misc
 fi
 
 # Register support for .NET CLR binaries
index 4e68849d56398d0f214fd9470b3e3ccfbc5409fb..688f18fd44676b2eb40d3239dd45702387793255 100644 (file)
@@ -93,7 +93,7 @@ Every time a driver is inserted into the kernel, it has to know which
 modems it should access at which ports. This can be done with the setbaycom
 utility. If you are only using one modem, you can also configure the
 driver from the insmod command line (or by means of an option line in
-/etc/modprobe.conf).
+/etc/modprobe.d/*.conf).
 
 Examples:
   modprobe baycom_ser_fdx mode="ser12*" iobase=0x3f8 irq=4
index 080ad26690ae436a6981883cb48b0a624f63f478..bfea8a338901900c78f82ae244603d8aa9069075 100644 (file)
@@ -173,9 +173,8 @@ bonding module at load time, or are specified via sysfs.
 
        Module options may be given as command line arguments to the
 insmod or modprobe command, but are usually specified in either the
-/etc/modules.conf or /etc/modprobe.conf configuration file, or in a
-distro-specific configuration file (some of which are detailed in the next
-section).
+/etc/modrobe.d/*.conf configuration files, or in a distro-specific
+configuration file (some of which are detailed in the next section).
 
        Details on bonding support for sysfs is provided in the
 "Configuring Bonding Manually via Sysfs" section, below.
@@ -1021,7 +1020,7 @@ ifcfg-bondX files.
 
        Because the sysconfig scripts supply the bonding module
 options in the ifcfg-bondX file, it is not necessary to add them to
-the system /etc/modules.conf or /etc/modprobe.conf configuration file.
+the system /etc/modules.d/*.conf configuration files.
 
 3.2 Configuration with Initscripts Support
 ------------------------------------------
@@ -1098,15 +1097,13 @@ queried targets, e.g.,
        arp_ip_target=+192.168.1.1 arp_ip_target=+192.168.1.2
 
        is the proper syntax to specify multiple targets.  When specifying
-options via BONDING_OPTS, it is not necessary to edit /etc/modules.conf or
-/etc/modprobe.conf.
+options via BONDING_OPTS, it is not necessary to edit /etc/modprobe.d/*.conf.
 
        For even older versions of initscripts that do not support
-BONDING_OPTS, it is necessary to edit /etc/modules.conf (or
-/etc/modprobe.conf, depending upon your distro) to load the bonding module
-with your desired options when the bond0 interface is brought up.  The
-following lines in /etc/modules.conf (or modprobe.conf) will load the
-bonding module, and select its options:
+BONDING_OPTS, it is necessary to edit /etc/modprobe.d/*.conf, depending upon
+your distro) to load the bonding module with your desired options when the
+bond0 interface is brought up.  The following lines in /etc/modprobe.d/*.conf
+will load the bonding module, and select its options:
 
 alias bond0 bonding
 options bond0 mode=balance-alb miimon=100
@@ -1152,7 +1149,7 @@ knowledge of bonding.  One such distro is SuSE Linux Enterprise Server
 version 8.
 
        The general method for these systems is to place the bonding
-module parameters into /etc/modules.conf or /etc/modprobe.conf (as
+module parameters into a config file in /etc/modprobe.d/ (as
 appropriate for the installed distro), then add modprobe and/or
 ifenslave commands to the system's global init script.  The name of
 the global init script differs; for sysconfig, it is
@@ -1228,7 +1225,7 @@ network initialization scripts.
 specify a different name for each instance (the module loading system
 requires that every loaded module, even multiple instances of the same
 module, have a unique name).  This is accomplished by supplying multiple
-sets of bonding options in /etc/modprobe.conf, for example:
+sets of bonding options in /etc/modprobe.d/*.conf, for example:
 
 alias bond0 bonding
 options bond0 -o bond0 mode=balance-rr miimon=100
@@ -1793,8 +1790,8 @@ route additions may cause trouble.
        On systems with network configuration scripts that do not
 associate physical devices directly with network interface names (so
 that the same physical device always has the same "ethX" name), it may
-be necessary to add some special logic to either /etc/modules.conf or
-/etc/modprobe.conf (depending upon which is installed on the system).
+be necessary to add some special logic to config files in
+/etc/modprobe.d/.
 
        For example, given a modules.conf containing the following:
 
@@ -1821,20 +1818,15 @@ add above bonding e1000 tg3
 bonding is loaded.  This command is fully documented in the
 modules.conf manual page.
 
-       On systems utilizing modprobe.conf (or modprobe.conf.local),
-an equivalent problem can occur.  In this case, the following can be
-added to modprobe.conf (or modprobe.conf.local, as appropriate), as
-follows (all on one line; it has been split here for clarity):
+       On systems utilizing modprobe an equivalent problem can occur.
+In this case, the following can be added to config files in
+/etc/modprobe.d/ as:
 
-install bonding /sbin/modprobe tg3; /sbin/modprobe e1000;
-       /sbin/modprobe --ignore-install bonding
+softdep bonding pre: tg3 e1000
 
-       This will, when loading the bonding module, rather than
-performing the normal action, instead execute the provided command.
-This command loads the device drivers in the order needed, then calls
-modprobe with --ignore-install to cause the normal action to then take
-place.  Full documentation on this can be found in the modprobe.conf
-and modprobe manual pages.
+       This will load tg3 and e1000 modules before loading the bonding one.
+Full documentation on this can be found in the modprobe.d and modprobe
+manual pages.
 
 8.3. Painfully Slow Or No Failed Link Detection By Miimon
 ---------------------------------------------------------
index 10e8490fa406b1bc3f237d89c822df0bbb68dbd9..cba74f7a3abc3c8f29bf4bcb37c205f061b0db1d 100644 (file)
@@ -45,12 +45,13 @@ Now eth0 should active, you can test it by "ping" or get more information by
 "ifconfig". If tested ok, continue the next step.
 
 4. cp dl2k.ko /lib/modules/`uname -r`/kernel/drivers/net
-5. Add the following line to /etc/modprobe.conf:
+5. Add the following line to /etc/modprobe.d/dl2k.conf:
        alias eth0 dl2k
-6. Run "netconfig" or "netconf" to create configuration script ifcfg-eth0
+6. Run depmod to updated module indexes.
+7. Run "netconfig" or "netconf" to create configuration script ifcfg-eth0
    located at /etc/sysconfig/network-scripts or create it manually.
    [see - Configuration Script Sample]
-7. Driver will automatically load and configure at next boot time.
+8. Driver will automatically load and configure at next boot time.
 
 Compiling the Driver
 ====================
@@ -154,8 +155,8 @@ Installing the Driver
   -----------------
   1. Copy dl2k.o to the network modules directory, typically
      /lib/modules/2.x.x-xx/net or /lib/modules/2.x.x/kernel/drivers/net.
-  2. Locate the boot module configuration file, most commonly modprobe.conf
-     or modules.conf (for 2.4) in the /etc directory. Add the following lines:
+  2. Locate the boot module configuration file, most commonly in the
+     /etc/modprobe.d/ directory. Add the following lines:
 
      alias ethx dl2k
      options dl2k <optional parameters>
index 162f323a7a1fef9379e44b6f915a6a0ae3d11f58..fcb6c71cdb69e7c0e643abb8124309adf4061b24 100644 (file)
@@ -94,8 +94,8 @@ Additional Configurations
 
   Configuring a network driver to load properly when the system is started is
   distribution dependent. Typically, the configuration process involves adding
-  an alias line to /etc/modules.conf or /etc/modprobe.conf as well as editing
-  other system startup scripts and/or configuration files.  Many popular Linux
+  an alias line to /etc/modprobe.d/*.conf as well as editing other system
+  startup scripts and/or configuration files.  Many popular Linux
   distributions ship with tools to make these changes for you. To learn the
   proper way to configure a network device for your system, refer to your
   distribution documentation.  If during this process you are asked for the
@@ -103,7 +103,7 @@ Additional Configurations
   PRO/100 Family of Adapters is e100.
 
   As an example, if you install the e100 driver for two PRO/100 adapters
-  (eth0 and eth1), add the following to modules.conf or modprobe.conf:
+  (eth0 and eth1), add the following to a configuraton file in /etc/modprobe.d/
 
        alias eth0 e100
        alias eth1 e100
index 9fd7e21296c8f500938e7bc272f3ef0528fc3917..6cd74fa55358bcfae3fa6a49805f0c62bc90470c 100644 (file)
@@ -2,9 +2,9 @@
 Options for the ipv6 module are supplied as parameters at load time.
 
 Module options may be given as command line arguments to the insmod
-or modprobe command, but are usually specified in either the
-/etc/modules.conf or /etc/modprobe.conf configuration file, or in a
-distro-specific configuration file.
+or modprobe command, but are usually specified in either
+/etc/modules.d/*.conf configuration files, or in a distro-specific
+configuration file.
 
 The available ipv6 module parameters are listed below.  If a parameter
 is not specified the default value is used.
index e196f16df313e3165afd5f843f4c9b459b3b3e7a..d75a1f9565bbfe5b8069ba05e36adf2619466171 100644 (file)
@@ -274,9 +274,9 @@ Additional Configurations
   -------------------------------------------------
   Configuring a network driver to load properly when the system is started is
   distribution dependent. Typically, the configuration process involves adding
-  an alias line to /etc/modprobe.conf as well as editing other system startup
-  scripts and/or configuration files.  Many popular Linux distributions ship
-  with tools to make these changes for you.  To learn the proper way to
+  an alias line to files in /etc/modprobe.d/ as well as editing other system
+  startup scripts and/or configuration files.  Many popular Linux distributions
+  ship with tools to make these changes for you.  To learn the proper way to
   configure a network device for your system, refer to your distribution
   documentation.  If during this process you are asked for the driver or module
   name, the name for the Linux Base Driver for the Intel 10GbE Family of
index fe2a9129d959557cb597911ea474b9d44710cc1e..0bf3220c715bfd68dbda3c69e6cdd61a3145d4b2 100644 (file)
@@ -25,7 +25,7 @@ the driver will try to determine them itself.
 
 If you load the driver as a module, you can pass the parameters "io=",
 "irq=", and "dma=" on the command line with insmod or modprobe, or add
-them as options in /etc/modprobe.conf:
+them as options in a configuration file in /etc/modprobe.d/ directory:
 
  alias lt0 ltpc # autoload the module when the interface is configured
  options ltpc io=0x240 irq=9 dma=1
index bd70976b816039e6e06c34e5f6924f0ee0130469..b4038ffb3bc57310055cc478266818c867484e0a 100644 (file)
@@ -67,8 +67,8 @@ Module parameters
 =================
 
 There are several parameters which may be provided to the driver when
-its module is loaded.  These are usually placed in /etc/modprobe.conf
-(/etc/modules.conf in 2.4).  Example:
+its module is loaded.  These are usually placed in /etc/modprobe.d/*.conf
+configuretion files.  Example:
 
 options 3c59x debug=3 rx_copybreak=300
 
@@ -425,7 +425,7 @@ steps you should take:
       1) Increase the debug level.  Usually this is done via:
 
          a) modprobe driver debug=7
-         b) In /etc/modprobe.conf (or /etc/modules.conf for 2.4):
+         b) In /etc/modprobe.d/driver.conf:
             options driver debug=7
 
       2) Recreate the problem with the higher debug level,
index 93a7ceef398d3841c95cb7e0fe17709511892f08..c208e4366c033d5bc5d1c40b6d055b7c722656d4 100644 (file)
@@ -36,18 +36,17 @@ addresses should not be specified for supported PCI cards since they
 are automatically detected.
 
 
-KMod
-----
+modprobe
+--------
 
-If you use kmod, you will find it useful to edit /etc/modprobe.conf.
-Here is an example of the lines that need to be added:
+If you use modprobe , you will find it useful to add lines as below to a
+configuration file in /etc/modprobe.d/ directory:.
 
        alias parport_lowlevel parport_pc
        options parport_pc io=0x378,0x278 irq=7,auto
 
-KMod will then automatically load parport_pc (with the options
-"io=0x378,0x278 irq=7,auto") whenever a parallel port device driver
-(such as lp) is loaded.
+modprobe will load parport_pc (with the options "io=0x378,0x278 irq=7,auto")
+whenever a parallel port device driver (such as lp) is loaded.
 
 Note that these are example lines only!  You shouldn't in general need
 to specify any options to parport_pc in order to be able to use a
index 7a5c73a7ed7f400694a8d97f0b43cfbef75fef67..7c715de997747eb2f7def3a8c7529ab7acd6105f 100644 (file)
@@ -47,9 +47,9 @@ including the console 3270, changes subchannel identifier relative to
 one another.  ReIPL as soon as possible after running the configuration
 script and the resulting /tmp/mkdev3270.
 
-If you have chosen to make tub3270 a module, you add a line to
-/etc/modprobe.conf.  If you are working on a VM virtual machine, you
-can use DEF GRAF to define virtual 3270 devices.
+If you have chosen to make tub3270 a module, you add a line to a
+configuration file under /etc/modprobe.d/.  If you are working on a VM
+virtual machine, you can use DEF GRAF to define virtual 3270 devices.
 
 You may generate both 3270 and 3215 console support, or one or the
 other, or neither.  If you generate both, the console type under VM is
@@ -60,7 +60,7 @@ at boot time to a 3270 if it is a 3215.
 
 In brief, these are the steps:
        1. Install the tub3270 patch
-       2. (If a module) add a line to /etc/modprobe.conf
+       2. (If a module) add a line to a file in /etc/modprobe.d/*.conf
        3. (If VM) define devices with DEF GRAF
        4. Reboot
        5. Configure
@@ -84,13 +84,12 @@ Here are the installation steps in detail:
                make modules_install
 
        2. (Perform this step only if you have configured tub3270 as a
-       module.)  Add a line to /etc/modprobe.conf to automatically
-       load the driver when it's needed.  With this line added,
-       you will see login prompts appear on your 3270s as soon as
-       boot is complete (or with emulated 3270s, as soon as you dial
-       into your vm guest using the command "DIAL <vmguestname>").
-       Since the line-mode major number is 227, the line to add to
-       /etc/modprobe.conf should be:
+       module.)  Add a line to a file /etc/modprobe.d/*.conf to automatically
+       load the driver when it's needed.  With this line added, you will see
+       login prompts appear on your 3270s as soon as boot is complete (or
+       with emulated 3270s, as soon as you dial into your vm guest using the
+       command "DIAL <vmguestname>").  Since the line-mode major number is
+       227, the line to add should be:
                alias char-major-227 tub3270
 
        3. Define graphic devices to your vm guest machine, if you
index b48ded55b555041bc638c0f003d6248217f60856..b7dd6502bec577a9830bad64000f537cebbe7b53 100644 (file)
@@ -94,3 +94,5 @@ sym53c8xx_2.txt
        - info on second generation driver for sym53c8xx based adapters
 tmscsim.txt
        - info on driver for AM53c974 based adapters
+ufs.txt
+       - info on Universal Flash Storage(UFS) and UFS host controller driver.
index 64ac7093c872040f327193cb2037276eab000f33..e2d3273000d44219e7254cb406ea9cae765900c1 100644 (file)
@@ -215,7 +215,7 @@ The following information is available in this file:
                  INCORRECTLY CAN RENDER YOUR SYSTEM INOPERABLE.
                  USE THEM WITH CAUTION. 
 
-   Edit the file "modprobe.conf" in the directory /etc and add/edit a
+   Put a .conf file in the /etc/modprobe.d/ directory and add/edit a
    line containing 'options aic79xx aic79xx=[command[,command...]]' where
    'command' is one or more of the following:
    -----------------------------------------------------------------
index 18f8d1905e6afec5e59655723d3864642d521bbd..7c5d0223d444d482d99f17fe74e80323d7989213 100644 (file)
@@ -190,7 +190,7 @@ The following information is available in this file:
                  INCORRECTLY CAN RENDER YOUR SYSTEM INOPERABLE.
                  USE THEM WITH CAUTION. 
 
-   Edit the file "modprobe.conf" in the directory /etc and add/edit a
+   Put a .conf file in the /etc/modprobe.d directory and add/edit a
    line containing 'options aic7xxx aic7xxx=[command[,command...]]' where
    'command' is one or more of the following:
    -----------------------------------------------------------------
index ad86c6d1e89856286c0162e28836db862de68bba..00c8ebb2fd18add53002258163d32fd86ca561bf 100644 (file)
@@ -66,7 +66,7 @@ recognized.
 If you want to have the module autoloaded on access to /dev/osst, you may
 add something like
 alias char-major-206 osst
-to your /etc/modprobe.conf (before 2.6: modules.conf).
+to a file under /etc/modprobe.d/ directory.
 
 You may find it convenient to create a symbolic link 
 ln -s nosst0 /dev/tape
index 691ca292c24d751050bcf4bf726e900c0070aa09..685bf3582abe6104f367a76d48e03c5185bce911 100644 (file)
@@ -390,6 +390,10 @@ MTSETDRVBUFFER
             MT_ST_SYSV sets the SYSV semantics (mode)
             MT_ST_NOWAIT enables immediate mode (i.e., don't wait for
                the command to finish) for some commands (e.g., rewind)
+            MT_ST_NOWAIT_EOF enables immediate filemark mode (i.e. when
+               writing a filemark, don't wait for it to complete). Please
+               see the BASICS note about MTWEOFI with respect to the
+               possible dangers of writing immediate filemarks.
             MT_ST_SILI enables setting the SILI bit in SCSI commands when
                reading in variable block mode to enhance performance when
                reading blocks shorter than the byte count; set this only
diff --git a/Documentation/scsi/ufs.txt b/Documentation/scsi/ufs.txt
new file mode 100644 (file)
index 0000000..41a6164
--- /dev/null
@@ -0,0 +1,133 @@
+                       Universal Flash Storage
+                       =======================
+
+
+Contents
+--------
+
+1. Overview
+2. UFS Architecture Overview
+  2.1 Application Layer
+  2.2 UFS Transport Protocol(UTP) layer
+  2.3 UFS Interconnect(UIC) Layer
+3. UFSHCD Overview
+  3.1 UFS controller initialization
+  3.2 UTP Transfer requests
+  3.3 UFS error handling
+  3.4 SCSI Error handling
+
+
+1. Overview
+-----------
+
+Universal Flash Storage(UFS) is a storage specification for flash devices.
+It is aimed to provide a universal storage interface for both
+embedded and removable flash memory based storage in mobile
+devices such as smart phones and tablet computers. The specification
+is defined by JEDEC Solid State Technology Association. UFS is based
+on MIPI M-PHY physical layer standard. UFS uses MIPI M-PHY as the
+physical layer and MIPI Unipro as the link layer.
+
+The main goals of UFS is to provide,
+ * Optimized performance:
+   For UFS version 1.0 and 1.1 the target performance is as follows,
+   Support for Gear1 is mandatory (rate A: 1248Mbps, rate B: 1457.6Mbps)
+   Support for Gear2 is optional (rate A: 2496Mbps, rate B: 2915.2Mbps)
+   Future version of the standard,
+   Gear3 (rate A: 4992Mbps, rate B: 5830.4Mbps)
+ * Low power consumption
+ * High random IOPs and low latency
+
+
+2. UFS Architecture Overview
+----------------------------
+
+UFS has a layered communication architecture which is based on SCSI
+SAM-5 architectural model.
+
+UFS communication architecture consists of following layers,
+
+2.1 Application Layer
+
+  The Application layer is composed of UFS command set layer(UCS),
+  Task Manager and Device manager. The UFS interface is designed to be
+  protocol agnostic, however SCSI has been selected as a baseline
+  protocol for versions 1.0 and 1.1 of UFS protocol  layer.
+  UFS supports subset of SCSI commands defined by SPC-4 and SBC-3.
+  * UCS: It handles SCSI commands supported by UFS specification.
+  * Task manager: It handles task management functions defined by the
+     UFS which are meant for command queue control.
+  * Device manager: It handles device level operations and device
+     configuration operations. Device level operations mainly involve
+     device power management operations and commands to Interconnect
+     layers. Device level configurations involve handling of query
+     requests which are used to modify and retrieve configuration
+     information of the device.
+
+2.2 UFS Transport Protocol(UTP) layer
+
+  UTP layer provides services for
+  the higher layers through Service Access Points. UTP defines 3
+  service access points for higher layers.
+  * UDM_SAP: Device manager service access point is exposed to device
+    manager for device level operations. These device level operations
+    are done through query requests.
+  * UTP_CMD_SAP: Command service access point is exposed to UFS command
+    set layer(UCS) to transport commands.
+  * UTP_TM_SAP: Task management service access point is exposed to task
+    manager to transport task management functions.
+  UTP transports messages through UFS protocol information unit(UPIU).
+
+2.3 UFS Interconnect(UIC) Layer
+
+  UIC is the lowest layer of UFS layered architecture. It handles
+  connection between UFS host and UFS device. UIC consists of
+  MIPI UniPro and MIPI M-PHY. UIC provides 2 service access points
+  to upper layer,
+  * UIC_SAP: To transport UPIU between UFS host and UFS device.
+  * UIO_SAP: To issue commands to Unipro layers.
+
+
+3. UFSHCD Overview
+------------------
+
+The UFS host controller driver is based on Linux SCSI Framework.
+UFSHCD is a low level device driver which acts as an interface between
+SCSI Midlayer and PCIe based UFS host controllers.
+
+The current UFSHCD implementation supports following functionality,
+
+3.1 UFS controller initialization
+
+  The initialization module brings UFS host controller to active state
+  and prepares the controller to transfer commands/response between
+  UFSHCD and UFS device.
+
+3.2 UTP Transfer requests
+
+  Transfer request handling module of UFSHCD receives SCSI commands
+  from SCSI Midlayer, forms UPIUs and issues the UPIUs to UFS Host
+  controller. Also, the module decodes, responses received from UFS
+  host controller in the form of UPIUs and intimates the SCSI Midlayer
+  of the status of the command.
+
+3.3 UFS error handling
+
+  Error handling module handles Host controller fatal errors,
+  Device fatal errors and UIC interconnect layer related errors.
+
+3.4 SCSI Error handling
+
+  This is done through UFSHCD SCSI error handling routines registered
+  with SCSI Midlayer. Examples of some of the error handling commands
+  issues by SCSI Midlayer are Abort task, Lun reset and host reset.
+  UFSHCD Routines to perform these tasks are registered with
+  SCSI Midlayer through .eh_abort_handler, .eh_device_reset_handler and
+  .eh_host_reset_handler.
+
+In this version of UFSHCD Query requests and power management
+functionality are not implemented.
+
+UFS Specifications can be found at,
+UFS - http://www.jedec.org/sites/default/files/docs/JESD220.pdf
+UFSHCI - http://www.jedec.org/sites/default/files/docs/JESD223.pdf
index 39ddcdbeeb854bbcf447e26d5505e4352379bde6..a6a1158ea2ba9839e64b84bfa94ee4e960da90d9 100644 (file)
@@ -49,7 +49,7 @@ Hardware - If you have an ISA card, find a free interrupt and io port.
 
        Note the hardware address from the Computone ISA cards installed into
                the system.  These are required for editing ip2.c or editing
-               /etc/modprobe.conf, or for specification on the modprobe
+               /etc/modprobe.d/*.conf, or for specification on the modprobe
                command line.
 
        Note that the /etc/modules.conf should be used for older (pre-2.6)
@@ -66,7 +66,7 @@ b) Run "make config" or "make menuconfig" or "make xconfig"
 c) Set address on ISA cards then:
    edit /usr/src/linux/drivers/char/ip2.c if needed 
        or
-   edit /etc/modprobe.conf if needed (module).
+   edit config file in  /etc/modprobe.d/ if needed (module).
        or both to match this setting.
 d) Run "make modules"
 e) Run "make modules_install"
@@ -153,11 +153,11 @@ the irqs are not specified the driver uses the default in ip2.c (which
 selects polled mode). If no base addresses are specified the defaults in 
 ip2.c are used. If you are autoloading the driver module with kerneld or
 kmod the base addresses and interrupt number must also be set in ip2.c
-and recompile or just insert and options line in /etc/modprobe.conf or both.
+and recompile or just insert and options line in /etc/modprobe.d/*.conf or both.
 The options line is equivalent to the command line and takes precedence over
 what is in ip2.c. 
 
-/etc/modprobe.conf sample:
+config sample to put /etc/modprobe.d/*.conf:
        options ip2 io=1,0x328 irq=1,10
        alias char-major-71 ip2
        alias char-major-72 ip2
index 1d8582990435b55579c8396507c875121a411ad7..60b0398910576d6f9eb714010ed7a6bdc8596721 100644 (file)
@@ -62,7 +62,7 @@ in the system log at /var/log/messages.
 
 If installed as a module, the module must be loaded.  This can be done
 manually by entering "modprobe rocket".  To have the module loaded automatically
-upon system boot, edit the /etc/modprobe.conf file and add the line
+upon system boot, edit a /etc/modprobe.d/*.conf file and add the line
 "alias char-major-46 rocket".
 
 In order to use the ports, their device names (nodes) must be created with mknod.
index 5c4902d9a5beca1e45d24e4a16e0200ba73c2de0..55090914a9c56e5339c33b2b964d61e50b7fe722 100644 (file)
@@ -139,8 +139,8 @@ secondary address 0x280 and IRQ 10.
 
 You will probably want to enter this module load and configuration information
 into your system startup scripts so that the drivers are loaded and configured
-on each system boot. Typically the start up script would be something like
-/etc/modprobe.conf.
+on each system boot. Typically configuration files are put in the
+/etc/modprobe.d/ directory.
 
 
 2.2 STATIC DRIVER CONFIGURATION:
index 6f75ba3b8a391f634de7bd77ea2cae1392763571..8c16d50f6cb6f67284220f5fb29785224b48965a 100644 (file)
@@ -2044,7 +2044,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     Install the necessary firmware files in alsa-firmware package.
     When no hotplug fw loader is available, you need to load the
     firmware via vxloader utility in alsa-tools package.  To invoke
-    vxloader automatically, add the following to /etc/modprobe.conf
+    vxloader automatically, add the following to /etc/modprobe.d/alsa.conf
 
        install snd-vx222 /sbin/modprobe --first-time -i snd-vx222 && /usr/bin/vxloader
 
@@ -2168,10 +2168,10 @@ corresponds to the card index of ALSA.  Usually, define this
 as the same card module.
 
 An example configuration for a single emu10k1 card is like below:
------ /etc/modprobe.conf
+----- /etc/modprobe.d/alsa.conf
 alias snd-card-0 snd-emu10k1
 alias sound-slot-0 snd-emu10k1
------ /etc/modprobe.conf
+----- /etc/modprobe.d/alsa.conf
 
 The available number of auto-loaded sound cards depends on the module
 option "cards_limit" of snd module.  As default it's set to 1.
@@ -2184,7 +2184,7 @@ cards is kept consistent.
 
 An example configuration for two sound cards is like below:
 
------ /etc/modprobe.conf
+----- /etc/modprobe.d/alsa.conf
 # ALSA portion
 options snd cards_limit=2
 alias snd-card-0 snd-interwave
@@ -2194,7 +2194,7 @@ options snd-ens1371 index=1
 # OSS/Free portion
 alias sound-slot-0 snd-interwave
 alias sound-slot-1 snd-ens1371
------ /etc/modprobe.conf
+----- /etc/modprobe.d/alsa.conf
 
 In this example, the interwave card is always loaded as the first card
 (index 0) and ens1371 as the second (index 1).
index a4c53d8961e1ca1c57686ac9560bece125fed03a..654dd3b694a8f3034a450a2e479062c9ee34f307 100644 (file)
@@ -232,7 +232,7 @@ The parameter can be given:
    # modprobe snd-usb-audio index=1 device_setup=0x09
 
  * Or while configuring the modules options in your modules configuration file
-   - For Fedora distributions, edit the /etc/modprobe.conf file:
+   (tipically a .conf file in /etc/modprobe.d/ directory:
        alias snd-card-1 snd-usb-audio
        options snd-usb-audio index=1 device_setup=0x09
 
@@ -253,7 +253,7 @@ CAUTION when initializing the device
    - first turn off the device
    - de-register the snd-usb-audio module (modprobe -r)
    - change the device_setup parameter by changing the device_setup
-     option in /etc/modprobe.conf 
+     option in /etc/modprobe.d/*.conf
    - turn on the device
  * A workaround for this last issue has been applied to kernel 2.6.23, but it may not
    be enough to ensure the 'stability' of the device initialization.
index ef42c44fa1f238b7df24b9dbc06b56574c3dd4e1..4ee35b4fbe4a11adbf4b77ce848fbd70f75c9a18 100644 (file)
@@ -76,9 +76,9 @@ FIRMWARE
  when CONFIG_FW_LOADER is set.  The mixartloader is necessary only
  for older versions or when you build the driver into kernel.]
  
-For loading the firmware automatically after the module is loaded, use
-the post-install command.  For example, add the following entry to
-/etc/modprobe.conf for miXart driver:
+For loading the firmware automatically after the module is loaded, use a
+install command.  For example, add the following entry to
+/etc/modprobe.d/mixart.conf for miXart driver:
 
        install snd-mixart /sbin/modprobe --first-time -i snd-mixart && \
                           /usr/bin/mixartloader
index 022aaeb0e9ddf3b461d1db2f63c6927d22479373..152ca2a3f1bd94d3bd6a191dbb0e90691758f7de 100644 (file)
@@ -19,7 +19,7 @@ the card number and the minor unit number.  Usually you don't have to
 define these aliases by yourself.
 
 Only necessary step for auto-loading of OSS modules is to define the
-card alias in /etc/modprobe.conf, such as
+card alias in /etc/modprobe.d/alsa.conf, such as
 
        alias sound-slot-0 snd-emu10k1
 
index e0dc0641b480e0c8c3f12e7d73cb880c37c23eba..ea8549faede9d7c1dd35e878632c32ec3886b28a 100644 (file)
@@ -41,7 +41,7 @@ mpu_base      I/O base address for activate MPU-401 mode
                (0x300, 0x310, 0x320 or 0x330)
 mpu_irq                MPU-401 irq line (5, 7, 9, 10 or 0)
 
-The /etc/modprobe.conf will have lines like this:
+A configuration file in /etc/modprobe.d/ directory will have lines like this:
 
 options opl3 io=0x388
 options ad1848 io=0x530 irq=11 dma=3
@@ -51,11 +51,11 @@ Where the aedsp16 options are the options for this driver while opl3 and
 ad1848 are the corresponding options for the MSS and OPL3 modules.
 
 Loading MSS and OPL3 needs to pre load the aedsp16 module to set up correctly
-the sound card. Installation dependencies must be written in the modprobe.conf
-file:
+the sound card. Installation dependencies must be written in configuration
+files under /etc/modprobe.d/ directory:
 
-install ad1848 /sbin/modprobe aedsp16 && /sbin/modprobe -i ad1848
-install opl3 /sbin/modprobe aedsp16 && /sbin/modprobe -i opl3
+softdep ad1848 pre: aedsp16
+softdep opl3 pre: aedsp16
 
 Then you must load the sound modules stack in this order:
 sound -> aedsp16 -> [ ad1848, opl3 ]
index 9c439f1a6dba3b901d3184079b34cf95915f3201..8a5fd1611c6f2590194ff3d39a3c7b3846e15853 100644 (file)
@@ -143,11 +143,10 @@ CONFIG_SOUND_MSS=m
 
 
 
-Alma Chao <elysian@ethereal.torsion.org> suggests the following /etc/modprobe.conf:
+Alma Chao <elysian@ethereal.torsion.org> suggests the following in
+a /etc/modprobe.d/*conf file:
 
 alias sound ad1848
 alias synth0 opl3
 options ad1848 io=0x530 irq=7 dma=0 soundpro=1
 options opl3 io=0x388
-
-       
index 75d967ff92663dd7fd72db9be9ef8926d8e6cf7e..42da2d8fa37222269c3ba8c0f8577baf5c1e85a5 100644 (file)
@@ -167,8 +167,8 @@ in a file such as /root/soundon.sh.
 MODPROBE:
 =========
 
-If loading via modprobe, these common files are automatically loaded 
-when requested by modprobe.  For example, my /etc/modprobe.conf contains:
+If loading via modprobe, these common files are automatically loaded when
+requested by modprobe.  For example, my /etc/modprobe.d/oss.conf contains:
 
 alias sound sb 
 options sb io=0x240 irq=9 dma=3 dma16=5 mpu_io=0x300
@@ -228,7 +228,7 @@ http://www.opensound.com.  Before loading the commercial sound
 driver, you should do the following:
 
 1.  remove sound modules (detailed above)
-2.  remove the sound modules from /etc/modprobe.conf
+2.  remove the sound modules from /etc/modprobe.d/*.conf
 3.  move the sound modules from /lib/modules/<kernel>/misc
     (for example, I make a /lib/modules/<kernel>/misc/tmp
     directory and copy the sound module files to that 
@@ -265,7 +265,7 @@ twice, you need to do the following:
     sb.o could be copied (or symlinked) to sb1.o for the
     second SoundBlaster.
 
-2.  Make a second entry in /etc/modprobe.conf, for example,
+2.  Make a second entry in /etc/modprobe.d/*conf, for example,
     sound1 or sb1.  This second entry should refer to the
     new module names for example sb1, and should include
     the I/O, etc. for the second sound card.
@@ -369,7 +369,7 @@ There are several ways of configuring your sound:
 2)  On the command line when using insmod or in a bash script
     using command line calls to load sound.
 
-3)  In /etc/modprobe.conf when using modprobe.
+3)  In /etc/modprobe.d/*conf when using modprobe.
 
 4)  Via Red Hat's GPL'd /usr/sbin/sndconfig program (text based).
 
index c15af3c07d461c532f49f5c7030332907a81a0bb..4cd5d9ab35803c827ac89c6410d5d2e5fb7c5c3e 100644 (file)
@@ -18,7 +18,7 @@ force the card into a mode in which it can be programmed.
 If you have another OS installed on your computer it is recommended
 that Linux and the other OS use the same resources.
 
-Also, it is recommended that resources specified in /etc/modprobe.conf
+Also, it is recommended that resources specified in /etc/modprobe.d/*.conf
 and resources specified in /etc/isapnp.conf agree.
 
 Compiling the sound driver
@@ -67,11 +67,7 @@ address is hard-coded into the driver.
 
 Using kmod and autoloading the sound driver
 -------------------------------------------
-Comment: as of linux-2.1.90 kmod is replacing kerneld.
-The config file '/etc/modprobe.conf' is used as before.
-
-This is the sound part of my /etc/modprobe.conf file.
-Following that I will explain each line.
+Config files in '/etc/modprobe.d/' are used as below:
 
 alias mixer0 mad16
 alias audio0 mad16
index 3dca4b75988e77cc0da179abd42969165975e37c..5c27229eec8ca14f610e2d5401593e7d80e13ea3 100644 (file)
@@ -128,7 +128,7 @@ CONFIG_SOUND_YM3812
   You can then get OPL3 functionality by issuing the command:
   insmod opl3
   In addition, you must either add the following line to 
-  /etc/modprobe.conf:
+  /etc/modprobe.d/*.conf:
   options opl3 io=0x388
   or else add the following line to /etc/lilo.conf:
   opl3=0x388
@@ -158,5 +158,5 @@ following line would be appropriate:
 append="pas2=0x388,10,3,-1,0,-1,-1,-1 opl3=0x388"
 
 If sound is built totally modular, the above options may be 
-specified in /etc/modprobe.conf for pas2, sb and opl3
+specified in /etc/modprobe.d/*.conf for pas2, sb and opl3
 respectively. 
index e691d74e1e5eb4bd9f0da3ffbab084a44a15ce94..cdc039421a4606d73bb85d8a71986345369cf0e2 100644 (file)
@@ -26,7 +26,7 @@ Note that it is no longer necessary or possible to configure sound in the
 drivers/sound dir. Now one simply configures and makes one's kernel and
 modules in the usual way.
 
- Then, add to your /etc/modprobe.conf something like:
+ Then, add to your /etc/modprobe.d/oss.conf something like:
 
 alias char-major-14-* sb
 install sb /sbin/modprobe -i sb && /sbin/modprobe adlib_card
@@ -36,7 +36,7 @@ options adlib_card io=0x388     # FM synthesizer
  Alternatively, if you have compiled in kernel level ISAPnP support:
 
 alias char-major-14 sb
-post-install sb /sbin/modprobe "-k" "adlib_card"
+softdep sb post: adlib_card
 options adlib_card io=0x388
 
   The effect of this is that the sound driver and all necessary bits and
@@ -66,12 +66,12 @@ args are expected.
  Note that at present there is no way to configure the io, irq and other
 parameters for the modular drivers as one does for the wired drivers.. One
 needs to pass the modules the necessary parameters as arguments, either
-with /etc/modprobe.conf or with command-line args to modprobe, e.g.
+with /etc/modprobe.d/*.conf or with command-line args to modprobe, e.g.
 
 modprobe sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330
 modprobe adlib_card io=0x388
 
- recommend using /etc/modprobe.conf.
+ recommend using /etc/modprobe.d/*.conf.
 
 Persistent DMA Buffers:
 
@@ -89,7 +89,7 @@ wasteful of RAM, but it guarantees that sound always works.
 
 To make the sound driver use persistent DMA buffers we need to pass the
 sound.o module a "dmabuf=1" command-line argument. This is normally done
-in /etc/modprobe.conf like so:
+in /etc/modprobe.d/*.conf files like so:
 
 options sound          dmabuf=1
 
index 312e3754e8c5599b00c644d64be5b9907bb64ada..642f84495b29888c5ff937d3b5f01faef5674ecc 100644 (file)
@@ -241,9 +241,8 @@ command you are interested in.
 
 *  I have more questions, who can I ask?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-And I'll answer any questions about the registration system you got, also
-responding as soon as possible.
- -Crutcher
+Just ask them on the linux-kernel mailing list:
+       linux-kernel@vger.kernel.org
 
 *  Credits
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 817df299ea07918b138da483de5bd3be5407e51d..4204eb01fd3839187da2d20654a39f1f9a134b7e 100644 (file)
@@ -179,7 +179,8 @@ do:
 
        modprobe usbcore autosuspend=5
 
-Equivalently, you could add to /etc/modprobe.conf a line saying:
+Equivalently, you could add to a configuration file in /etc/modprobe.d
+a line saying:
 
        options usbcore autosuspend=5
 
index 8977e7ce4dab433db2e4bb660d54d20dd71e8768..6e680fec1e9c1d5e43a39e3e160572beb7c1b4c2 100644 (file)
@@ -61,29 +61,19 @@ But that is my personal preference.
 2.2 Configuration
 
   The configuration requires module configuration and device
-configuration.  I like kmod or kerneld process with the
-/etc/modprobe.conf file so the modules can automatically load/unload as
-they are used.  The video devices could already exist, be generated
-using MAKEDEV, or need to be created.  The following sections detail
-these procedures.
+configuration.  The following sections detail these procedures.
 
 
 2.1 Module Configuration
 
   Using modules requires a bit of work to install and pass the
-parameters.  Understand that entries in /etc/modprobe.conf of:
+parameters.  Understand that entries in /etc/modprobe.d/*.conf of:
 
    alias parport_lowlevel parport_pc
    options parport_pc io=0x378 irq=none
    alias char-major-81 videodev
    alias char-major-81-0 c-qcam
 
-will cause the kmod/modprobe to do certain things.  If you are
-using kmod, then a request for a 'char-major-81-0' will cause
-the 'c-qcam' module to load.  If you have other video sources with
-modules, you might want to assign the different minor numbers to
-different modules.
-
 2.2 Device Configuration
 
   At this point, we need to ensure that the device files exist.
index 9ed629d4874bc4cd1f67d41f00646a4ba6929719..b5a911fd0602b45446aad56ad17de3e3b9c0577a 100644 (file)
@@ -255,7 +255,7 @@ Load zr36067.o. If it can't autodetect your card, use the card=X insmod
 option with X being the card number as given in the previous section.
 To have more than one card, use card=X1[,X2[,X3,[X4[..]]]]
 
-To automate this, add the following to your /etc/modprobe.conf:
+To automate this, add the following to your /etc/modprobe.d/zoran.conf:
 
 options zr36067 card=X1[,X2[,X3[,X4[..]]]]
 alias char-major-81-0 zr36067
index 753f15956eb8660ba9be5b89c83fddb5ee884ee5..8f258faf18f12df31b1f08f1ba4d7fbb91fdd3c7 100644 (file)
@@ -1,4 +1,4 @@
-# For modern kernels (2.6 or above), this belongs in /etc/modprobe.conf
+# For modern kernels (2.6 or above), this belongs in /etc/modprobe.d/*.conf
 # For for 2.4 kernels or earlier, this belongs in /etc/modules.conf.
 
 # i2c
index 34e2842c70aee9f645a2c83435b286ad03b804cb..a051152ea99c27b895890a2d94d27743b17cec3d 100644 (file)
@@ -55,7 +55,7 @@ Module use:
 -----------
 
 In order to automatically load the meye module on use, you can put those lines
-in your /etc/modprobe.conf file:
+in your /etc/modprobe.d/meye.conf file:
 
        alias char-major-81 videodev
        alias char-major-81-0 meye
index 64168f6dd89ee0d8b6de21f53f64d6b30bc08f38..eecf3441ac2154c3806bc5b7f1f7cfc213a5f920 100644 (file)
@@ -1940,7 +1940,7 @@ F:        drivers/connector/
 
 CONTROL GROUPS (CGROUPS)
 M:     Tejun Heo <tj@kernel.org>
-M:     Li Zefan <lizf@cn.fujitsu.com>
+M:     Li Zefan <lizefan@huawei.com>
 L:     containers@lists.linux-foundation.org
 L:     cgroups@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git
index 1932984478c177dc1905470252c65afa56f07559..fd4b3d038d757701bd94e2a9acdb462453a3196c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1170,7 +1170,7 @@ MRPROPER_FILES += .config .config.old .version .old_version             \
 #
 clean: rm-dirs  := $(CLEAN_DIRS)
 clean: rm-files := $(CLEAN_FILES)
-clean-dirs      := $(addprefix _clean_, . $(vmlinux-alldirs) Documentation)
+clean-dirs      := $(addprefix _clean_, . $(vmlinux-alldirs) Documentation samples)
 
 PHONY += $(clean-dirs) clean archclean
 $(clean-dirs):
index fc871e719aae8bd6e94d5602591f2948f568f8e9..c877087d2000cf054be2c086f3de957555f46066 100644 (file)
@@ -11,8 +11,6 @@
 # Copyright (C) 1995-2002 Russell King
 #
 
-MKIMAGE         := $(srctree)/scripts/mkuboot.sh
-
 ifneq ($(MACHINE),)
 include $(srctree)/$(MACHINE)/Makefile.boot
 endif
@@ -69,22 +67,19 @@ $(obj)/dtbs: $(addprefix $(obj)/, $(dtb-y))
 
 clean-files := *.dtb
 
-quiet_cmd_uimage = UIMAGE  $@
-      cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \
-                  -C none -a $(LOADADDR) -e $(STARTADDR) \
-                  -n 'Linux-$(KERNELRELEASE)' -d $< $@
-
-ifeq ($(CONFIG_ZBOOT_ROM),y)
-$(obj)/uImage: LOADADDR=$(CONFIG_ZBOOT_ROM_TEXT)
+ifneq ($(LOADADDR),)
+  UIMAGE_LOADADDR=$(LOADADDR)
 else
-$(obj)/uImage: LOADADDR=$(ZRELADDR)
+  ifeq ($(CONFIG_ZBOOT_ROM),y)
+    UIMAGE_LOADADDR=$(CONFIG_ZBOOT_ROM_TEXT)
+  else
+    UIMAGE_LOADADDR=$(ZRELADDR)
+  endif
 endif
 
-$(obj)/uImage: STARTADDR=$(LOADADDR)
-
 check_for_multiple_loadaddr = \
-if [ $(words $(LOADADDR)) -gt 1 ]; then \
-       echo 'multiple load addresses: $(LOADADDR)'; \
+if [ $(words $(UIMAGE_LOADADDR)) -gt 1 ]; then \
+       echo 'multiple load addresses: $(UIMAGE_LOADADDR)'; \
        echo 'This is incompatible with uImages'; \
        echo 'Specify LOADADDR on the commandline to build an uImage'; \
        false; \
diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h
new file mode 100644 (file)
index 0000000..2fca60a
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __ASM_ARM_CPUIDLE_H
+#define __ASM_ARM_CPUIDLE_H
+
+#ifdef CONFIG_CPU_IDLE
+extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
+               struct cpuidle_driver *drv, int index);
+#else
+static inline int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
+               struct cpuidle_driver *drv, int index) { return -ENODEV; }
+#endif
+
+/* Common ARM WFI state */
+#define ARM_CPUIDLE_WFI_STATE_PWR(p) {\
+       .enter                  = arm_cpuidle_simple_enter,\
+       .exit_latency           = 1,\
+       .target_residency       = 1,\
+       .power_usage            = p,\
+       .flags                  = CPUIDLE_FLAG_TIME_VALID,\
+       .name                   = "WFI",\
+       .desc                   = "ARM WFI",\
+}
+
+/*
+ * in case power_specified == 1, give a default WFI power value needed
+ * by some governors
+ */
+#define ARM_CPUIDLE_WFI_STATE ARM_CPUIDLE_WFI_STATE_PWR(UINT_MAX)
+
+#endif
index 8269d892874232f59f6e49537dc6df49e8c0927b..7b787d642af4fe2ac3c9f81be97c8913ea8b2e95 100644 (file)
@@ -23,7 +23,7 @@ obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += compat.o
 
 obj-$(CONFIG_LEDS)             += leds.o
 obj-$(CONFIG_OC_ETM)           += etm.o
-
+obj-$(CONFIG_CPU_IDLE)         += cpuidle.o
 obj-$(CONFIG_ISA_DMA_API)      += dma.o
 obj-$(CONFIG_FIQ)              += fiq.o fiqasm.o
 obj-$(CONFIG_MODULES)          += armksyms.o module.o
diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c
new file mode 100644 (file)
index 0000000..89545f6
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2012 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/cpuidle.h>
+#include <asm/proc-fns.h>
+
+int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
+               struct cpuidle_driver *drv, int index)
+{
+       cpu_do_idle();
+
+       return index;
+}
index 5400a1d6503566d7258a801e6b8aca3e6c636021..d62fe090d814c4860403984e470d2f6ad919a79f 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/irq.h>
 #include <asm/proc-fns.h>
+#include <asm/system_misc.h>
 #include <asm/mach/arch.h>
 #include <mach/at91x40.h>
 #include <mach/at91_st.h>
index 555d956b3a574b3a4d96be46c199c087d991080d..ece1f9aefb47a0ff46a0fe0f0bfeedd4d2bac27f 100644 (file)
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/cpuidle.h>
-#include <asm/proc-fns.h>
 #include <linux/io.h>
 #include <linux/export.h>
+#include <asm/proc-fns.h>
+#include <asm/cpuidle.h>
 
 #include "pm.h"
 
 
 static DEFINE_PER_CPU(struct cpuidle_device, at91_cpuidle_device);
 
-static struct cpuidle_driver at91_idle_driver = {
-       .name =         "at91_idle",
-       .owner =        THIS_MODULE,
-};
-
 /* Actual code that puts the SoC in different idle states */
 static int at91_enter_idle(struct cpuidle_device *dev,
                        struct cpuidle_driver *drv,
                               int index)
 {
-       struct timeval before, after;
-       int idle_time;
-
-       local_irq_disable();
-       do_gettimeofday(&before);
-       if (index == 0)
-               /* Wait for interrupt state */
-               cpu_do_idle();
-       else if (index == 1)
-               at91_standby();
+       at91_standby();
 
-       do_gettimeofday(&after);
-       local_irq_enable();
-       idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
-                       (after.tv_usec - before.tv_usec);
-
-       dev->last_residency = idle_time;
        return index;
 }
 
+static struct cpuidle_driver at91_idle_driver = {
+       .name                   = "at91_idle",
+       .owner                  = THIS_MODULE,
+       .en_core_tk_irqen       = 1,
+       .states[0]              = ARM_CPUIDLE_WFI_STATE,
+       .states[1]              = {
+               .enter                  = at91_enter_idle,
+               .exit_latency           = 10,
+               .target_residency       = 100000,
+               .flags                  = CPUIDLE_FLAG_TIME_VALID,
+               .name                   = "RAM_SR",
+               .desc                   = "WFI and DDR Self Refresh",
+       },
+       .state_count = AT91_MAX_STATES,
+};
+
 /* Initialize CPU idle by registering the idle states */
 static int at91_init_cpuidle(void)
 {
        struct cpuidle_device *device;
-       struct cpuidle_driver *driver = &at91_idle_driver;
 
        device = &per_cpu(at91_cpuidle_device, smp_processor_id());
        device->state_count = AT91_MAX_STATES;
-       driver->state_count = AT91_MAX_STATES;
-
-       /* Wait for interrupt state */
-       driver->states[0].enter = at91_enter_idle;
-       driver->states[0].exit_latency = 1;
-       driver->states[0].target_residency = 10000;
-       driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
-       strcpy(driver->states[0].name, "WFI");
-       strcpy(driver->states[0].desc, "Wait for interrupt");
-
-       /* Wait for interrupt and RAM self refresh state */
-       driver->states[1].enter = at91_enter_idle;
-       driver->states[1].exit_latency = 10;
-       driver->states[1].target_residency = 10000;
-       driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
-       strcpy(driver->states[1].name, "RAM_SR");
-       strcpy(driver->states[1].desc, "WFI and RAM Self Refresh");
 
        cpuidle_register_driver(&at91_idle_driver);
 
index 1083739e30650185ccaed48d7f242e1bee4ce506..97cc04dc807302d635e9fe56176d34e0d9210c4f 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/pm.h>
 #include <linux/of_address.h>
 
+#include <asm/system_misc.h>
 #include <asm/mach/map.h>
 
 #include <mach/hardware.h>
index 0bea1454ae03fff25ee689b61e3999a826559a36..4372f06c9929657ac423fc4b0c84e78f170e0566 100644 (file)
@@ -21,6 +21,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/bug.h>
 
 #include <mach/hardware.h>
 #include <asm/page.h>
index a30c7c5a6d83a4703b968fe15212faec24ee6db1..9107691adbdb73673abe7ed7f66c420190b33af8 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/io.h>
 #include <linux/export.h>
 #include <asm/proc-fns.h>
+#include <asm/cpuidle.h>
 
 #include <mach/cpuidle.h>
 #include <mach/ddr2.h>
@@ -30,12 +31,43 @@ struct davinci_ops {
        u32 flags;
 };
 
+/* Actual code that puts the SoC in different idle states */
+static int davinci_enter_idle(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv,
+                                               int index)
+{
+       struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+       struct davinci_ops *ops = cpuidle_get_statedata(state_usage);
+
+       if (ops && ops->enter)
+               ops->enter(ops->flags);
+
+       index = cpuidle_wrap_enter(dev, drv, index,
+                               arm_cpuidle_simple_enter);
+
+       if (ops && ops->exit)
+               ops->exit(ops->flags);
+
+       return index;
+}
+
 /* fields in davinci_ops.flags */
 #define DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN        BIT(0)
 
 static struct cpuidle_driver davinci_idle_driver = {
-       .name   = "cpuidle-davinci",
-       .owner  = THIS_MODULE,
+       .name                   = "cpuidle-davinci",
+       .owner                  = THIS_MODULE,
+       .en_core_tk_irqen       = 1,
+       .states[0]              = ARM_CPUIDLE_WFI_STATE,
+       .states[1]              = {
+               .enter                  = davinci_enter_idle,
+               .exit_latency           = 10,
+               .target_residency       = 100000,
+               .flags                  = CPUIDLE_FLAG_TIME_VALID,
+               .name                   = "DDR SR",
+               .desc                   = "WFI and DDR Self Refresh",
+       },
+       .state_count = DAVINCI_CPUIDLE_MAX_STATES,
 };
 
 static DEFINE_PER_CPU(struct cpuidle_device, davinci_cpuidle_device);
@@ -77,41 +109,10 @@ static struct davinci_ops davinci_states[DAVINCI_CPUIDLE_MAX_STATES] = {
        },
 };
 
-/* Actual code that puts the SoC in different idle states */
-static int davinci_enter_idle(struct cpuidle_device *dev,
-                               struct cpuidle_driver *drv,
-                                               int index)
-{
-       struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
-       struct davinci_ops *ops = cpuidle_get_statedata(state_usage);
-       struct timeval before, after;
-       int idle_time;
-
-       local_irq_disable();
-       do_gettimeofday(&before);
-
-       if (ops && ops->enter)
-               ops->enter(ops->flags);
-       /* Wait for interrupt state */
-       cpu_do_idle();
-       if (ops && ops->exit)
-               ops->exit(ops->flags);
-
-       do_gettimeofday(&after);
-       local_irq_enable();
-       idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
-                       (after.tv_usec - before.tv_usec);
-
-       dev->last_residency = idle_time;
-
-       return index;
-}
-
 static int __init davinci_cpuidle_probe(struct platform_device *pdev)
 {
        int ret;
        struct cpuidle_device *device;
-       struct cpuidle_driver *driver = &davinci_idle_driver;
        struct davinci_cpuidle_config *pdata = pdev->dev.platform_data;
 
        device = &per_cpu(davinci_cpuidle_device, smp_processor_id());
@@ -123,27 +124,11 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev)
 
        ddr2_reg_base = pdata->ddr2_ctlr_base;
 
-       /* Wait for interrupt state */
-       driver->states[0].enter = davinci_enter_idle;
-       driver->states[0].exit_latency = 1;
-       driver->states[0].target_residency = 10000;
-       driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
-       strcpy(driver->states[0].name, "WFI");
-       strcpy(driver->states[0].desc, "Wait for interrupt");
-
-       /* Wait for interrupt and DDR self refresh state */
-       driver->states[1].enter = davinci_enter_idle;
-       driver->states[1].exit_latency = 10;
-       driver->states[1].target_residency = 10000;
-       driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
-       strcpy(driver->states[1].name, "DDR SR");
-       strcpy(driver->states[1].desc, "WFI and DDR Self Refresh");
        if (pdata->ddr2_pdown)
                davinci_states[1].flags |= DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN;
        cpuidle_set_statedata(&device->states_usage[1], &davinci_states[1]);
 
        device->state_count = DAVINCI_CPUIDLE_MAX_STATES;
-       driver->state_count = DAVINCI_CPUIDLE_MAX_STATES;
 
        ret = cpuidle_register_driver(&davinci_idle_driver);
        if (ret) {
index e756d1ac00c22b06c9cb278ffeb0e3a8b19664ac..aa1331e86bcf4fb52f255b45164c5783f01cc634 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/dma.h>
 #include <mach/hardware.h>
 #include <asm/irq.h>
+#include <asm/system_misc.h>
 #include <asm/mach/irq.h>
 #include <asm/mach/map.h>
 #include <mach/irqs.h>
index d534d7f988e0952f0d555c797c3620ff3b40ca1a..74127389e7abe1b06ca648138137e79d0aa7b38f 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/err.h>
 
 #include <asm/pgtable.h>
+#include <asm/system_misc.h>
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/mach/map.h>
 
index 51af9fa56944cf6daf19dc295e7d7ec7f1efd9d4..05250aed61fbbc7f73f9e7b1c5b4bca00000f6fd 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/init.h>
 #include <linux/clk.h>
 
+#include <asm/system_misc.h>
 #include <asm/mach/map.h>
 
 #include <mach/hardware.h>
index d2c2dc35cbdd7132d935fcf80ae321da3dbf3db1..d34542425990cceaaa14dbde752a53070f2dc4e5 100644 (file)
@@ -36,6 +36,7 @@
 #include <asm/irq.h>
 #include <asm/tlbflush.h>
 #include <asm/pgtable.h>
+#include <asm/system_misc.h>
 
 #include <asm/mach/map.h>
 #include <asm/mach/time.h>
index c60e7b86192c90dffd028f4b14bc1dae82a96c31..ebbd7fc90eb47488b320de65feee82ce578ff288 100644 (file)
@@ -37,6 +37,7 @@
 #include <asm/page.h>
 #include <asm/irq.h>
 #include <asm/sched_clock.h>
+#include <asm/system_misc.h>
 
 #include <asm/mach/map.h>
 #include <asm/mach/irq.h>
index 7088180b018b6087546b2c5761f9618baca27c6a..0f1710941878fdb0847bd97b53bee7bf9e0a519d 100644 (file)
 #include <linux/io.h>
 #include <linux/export.h>
 #include <asm/proc-fns.h>
+#include <asm/cpuidle.h>
 #include <mach/kirkwood.h>
 
 #define KIRKWOOD_MAX_STATES    2
 
-static struct cpuidle_driver kirkwood_idle_driver = {
-       .name =         "kirkwood_idle",
-       .owner =        THIS_MODULE,
-};
-
-static DEFINE_PER_CPU(struct cpuidle_device, kirkwood_cpuidle_device);
-
 /* Actual code that puts the SoC in different idle states */
 static int kirkwood_enter_idle(struct cpuidle_device *dev,
                                struct cpuidle_driver *drv,
                               int index)
 {
-       struct timeval before, after;
-       int idle_time;
-
-       local_irq_disable();
-       do_gettimeofday(&before);
-       if (index == 0)
-               /* Wait for interrupt state */
-               cpu_do_idle();
-       else if (index == 1) {
-               /*
-                * Following write will put DDR in self refresh.
-                * Note that we have 256 cycles before DDR puts it
-                * self in self-refresh, so the wait-for-interrupt
-                * call afterwards won't get the DDR from self refresh
-                * mode.
-                */
-               writel(0x7, DDR_OPERATION_BASE);
-               cpu_do_idle();
-       }
-       do_gettimeofday(&after);
-       local_irq_enable();
-       idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
-                       (after.tv_usec - before.tv_usec);
-
-       /* Update last residency */
-       dev->last_residency = idle_time;
+       writel(0x7, DDR_OPERATION_BASE);
+       cpu_do_idle();
 
        return index;
 }
 
+static struct cpuidle_driver kirkwood_idle_driver = {
+       .name                   = "kirkwood_idle",
+       .owner                  = THIS_MODULE,
+       .en_core_tk_irqen       = 1,
+       .states[0]              = ARM_CPUIDLE_WFI_STATE,
+       .states[1]              = {
+               .enter                  = kirkwood_enter_idle,
+               .exit_latency           = 10,
+               .target_residency       = 100000,
+               .flags                  = CPUIDLE_FLAG_TIME_VALID,
+               .name                   = "DDR SR",
+               .desc                   = "WFI and DDR Self Refresh",
+       },
+       .state_count = KIRKWOOD_MAX_STATES,
+};
+
+static DEFINE_PER_CPU(struct cpuidle_device, kirkwood_cpuidle_device);
+
 /* Initialize CPU idle by registering the idle states */
 static int kirkwood_init_cpuidle(void)
 {
        struct cpuidle_device *device;
-       struct cpuidle_driver *driver = &kirkwood_idle_driver;
 
        device = &per_cpu(kirkwood_cpuidle_device, smp_processor_id());
        device->state_count = KIRKWOOD_MAX_STATES;
-       driver->state_count = KIRKWOOD_MAX_STATES;
-
-       /* Wait for interrupt state */
-       driver->states[0].enter = kirkwood_enter_idle;
-       driver->states[0].exit_latency = 1;
-       driver->states[0].target_residency = 10000;
-       driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
-       strcpy(driver->states[0].name, "WFI");
-       strcpy(driver->states[0].desc, "Wait for interrupt");
-
-       /* Wait for interrupt and DDR self refresh state */
-       driver->states[1].enter = kirkwood_enter_idle;
-       driver->states[1].exit_latency = 10;
-       driver->states[1].target_residency = 10000;
-       driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
-       strcpy(driver->states[1].name, "DDR SR");
-       strcpy(driver->states[1].desc, "WFI and DDR Self Refresh");
 
        cpuidle_register_driver(&kirkwood_idle_driver);
        if (cpuidle_register_device(device)) {
index b7ef51119d37580839f9773431f69c4282bdbae0..2fc24ca12054dae290b8c3b34077699225883370 100644 (file)
@@ -1134,7 +1134,7 @@ static struct clk_lookup lookups[] = {
        _REGISTER_CLOCK(NULL, "i2s1_ck", clk_i2s1)
        _REGISTER_CLOCK("ts-lpc32xx", NULL, clk_tsc)
        _REGISTER_CLOCK("dev:mmc0", NULL, clk_mmc)
-       _REGISTER_CLOCK("lpc-net.0", NULL, clk_net)
+       _REGISTER_CLOCK("lpc-eth.0", NULL, clk_net)
        _REGISTER_CLOCK("dev:clcd", NULL, clk_lcd)
        _REGISTER_CLOCK("lpc32xx_udc", "ck_usbd", clk_usbd)
        _REGISTER_CLOCK("lpc32xx_rtc", NULL, clk_rtc)
index c3068622fdcbe8f06377a6f38984af5c39e77656..553a2e535764b54b83635ba7161e5f67fd1c0c1d 100644 (file)
@@ -245,8 +245,6 @@ static struct resource h2_smc91x_resources[] = {
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
-               .start  = OMAP_GPIO_IRQ(0),
-               .end    = OMAP_GPIO_IRQ(0),
                .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE,
        },
 };
@@ -359,11 +357,9 @@ static struct tps65010_board tps_board = {
 static struct i2c_board_info __initdata h2_i2c_board_info[] = {
        {
                I2C_BOARD_INFO("tps65010", 0x48),
-               .irq            = OMAP_GPIO_IRQ(58),
                .platform_data  = &tps_board,
        }, {
                I2C_BOARD_INFO("isp1301_omap", 0x2d),
-               .irq            = OMAP_GPIO_IRQ(2),
        },
 };
 
@@ -428,8 +424,12 @@ static void __init h2_init(void)
        omap_cfg_reg(E19_1610_KBR4);
        omap_cfg_reg(N19_1610_KBR5);
 
+       h2_smc91x_resources[1].start = gpio_to_irq(0);
+       h2_smc91x_resources[1].end = gpio_to_irq(0);
        platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices));
        omap_serial_init();
+       h2_i2c_board_info[0].irq = gpio_to_irq(58);
+       h2_i2c_board_info[1].irq = gpio_to_irq(2);
        omap_register_i2c_bus(1, 100, h2_i2c_board_info,
                              ARRAY_SIZE(h2_i2c_board_info));
        omap1_usb_init(&h2_usb_config);
index 64b8584f64cea13ba471b66890726d5199fd545f..4c19f4c06851ff2c15c41a93807f33d73364fbe3 100644 (file)
@@ -247,8 +247,6 @@ static struct resource smc91x_resources[] = {
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
-               .start  = OMAP_GPIO_IRQ(40),
-               .end    = OMAP_GPIO_IRQ(40),
                .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE,
        },
 };
@@ -338,7 +336,6 @@ static struct spi_board_info h3_spi_board_info[] __initdata = {
                .modalias       = "tsc2101",
                .bus_num        = 2,
                .chip_select    = 0,
-               .irq            = OMAP_GPIO_IRQ(H3_TS_GPIO),
                .max_speed_hz   = 16000000,
                /* .platform_data       = &tsc_platform_data, */
        },
@@ -374,11 +371,9 @@ static struct omap_lcd_config h3_lcd_config __initdata = {
 static struct i2c_board_info __initdata h3_i2c_board_info[] = {
        {
                I2C_BOARD_INFO("tps65013", 0x48),
-               /* .irq         = OMAP_GPIO_IRQ(??), */
        },
        {
                I2C_BOARD_INFO("isp1301_omap", 0x2d),
-               .irq            = OMAP_GPIO_IRQ(14),
        },
 };
 
@@ -420,10 +415,14 @@ static void __init h3_init(void)
        omap_cfg_reg(E19_1610_KBR4);
        omap_cfg_reg(N19_1610_KBR5);
 
+       smc91x_resources[1].start = gpio_to_irq(40);
+       smc91x_resources[1].end = gpio_to_irq(40);
        platform_add_devices(devices, ARRAY_SIZE(devices));
+       h3_spi_board_info[0].irq = gpio_to_irq(H3_TS_GPIO);
        spi_register_board_info(h3_spi_board_info,
                                ARRAY_SIZE(h3_spi_board_info));
        omap_serial_init();
+       h3_i2c_board_info[1].irq = gpio_to_irq(14);
        omap_register_i2c_bus(1, 100, h3_i2c_board_info,
                              ARRAY_SIZE(h3_i2c_board_info));
        omap1_usb_init(&h3_usb_config);
index 827d83a96af83a8510a9e680fbb3ad802b22e2ad..60c06ee23855d018198698dbd74110611447d8e6 100644 (file)
@@ -324,8 +324,6 @@ static struct platform_device gpio_leds_device = {
 
 static struct resource htcpld_resources[] = {
        [0] = {
-               .start  = OMAP_GPIO_IRQ(HTCHERALD_GIRQ_BTNS),
-               .end    = OMAP_GPIO_IRQ(HTCHERALD_GIRQ_BTNS),
                .flags  = IORESOURCE_IRQ,
        },
 };
@@ -450,7 +448,6 @@ static struct spi_board_info __initdata htcherald_spi_board_info[] = {
        {
                .modalias               = "ads7846",
                .platform_data          = &htcherald_ts_platform_data,
-               .irq                    = OMAP_GPIO_IRQ(HTCHERALD_GPIO_TS),
                .max_speed_hz           = 2500000,
                .bus_num                = 2,
                .chip_select            = 1,
@@ -576,6 +573,8 @@ static void __init htcherald_init(void)
        printk(KERN_INFO "HTC Herald init.\n");
 
        /* Do board initialization before we register all the devices */
+       htcpld_resources[0].start = gpio_to_irq(HTCHERALD_GIRQ_BTNS);
+       htcpld_resources[0].end = gpio_to_irq(HTCHERALD_GIRQ_BTNS);
        platform_add_devices(devices, ARRAY_SIZE(devices));
 
        htcherald_disable_watchdog();
@@ -583,6 +582,7 @@ static void __init htcherald_init(void)
        htcherald_usb_enable();
        omap1_usb_init(&htcherald_usb_config);
 
+       htcherald_spi_board_info[0].irq = gpio_to_irq(HTCHERALD_GPIO_TS);
        spi_register_board_info(htcherald_spi_board_info,
                ARRAY_SIZE(htcherald_spi_board_info));
 
index 61219182d16a5def3427d5b65c7812a36f8ba044..67d7fd57a692b7d7c209b01859ad0a97cc8c86f0 100644 (file)
@@ -248,8 +248,6 @@ static struct resource innovator1610_smc91x_resources[] = {
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
-               .start  = OMAP_GPIO_IRQ(0),
-               .end    = OMAP_GPIO_IRQ(0),
                .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE,
        },
 };
@@ -409,6 +407,8 @@ static void __init innovator_init(void)
 #endif
 #ifdef CONFIG_ARCH_OMAP16XX
        if (!cpu_is_omap1510()) {
+               innovator1610_smc91x_resources[1].start = gpio_to_irq(0);
+               innovator1610_smc91x_resources[1].end = gpio_to_irq(0);
                platform_add_devices(innovator1610_devices, ARRAY_SIZE(innovator1610_devices));
        }
 #endif
index fe95ec5f6f03f3597511be1a54c8cf8b90d464c8..d21dcc2fbc5af3a3b53477e8e735aa5bcb428447 100644 (file)
@@ -147,7 +147,6 @@ static struct spi_board_info nokia770_spi_board_info[] __initdata = {
                .bus_num        = 2,
                .chip_select    = 0,
                .max_speed_hz   = 2500000,
-               .irq            = OMAP_GPIO_IRQ(15),
                .platform_data  = &nokia770_ads7846_platform_data,
        },
 };
@@ -237,6 +236,7 @@ static void __init omap_nokia770_init(void)
        omap_writew((omap_readw(0xfffb5004) & ~2), 0xfffb5004);
 
        platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices));
+       nokia770_spi_board_info[1].irq = gpio_to_irq(15);
        spi_register_board_info(nokia770_spi_board_info,
                                ARRAY_SIZE(nokia770_spi_board_info));
        omap_serial_init();
index 1fe347396f4d05a4b694f0e9476445c81a908417..a5f85dda3f6924ce84dce90270a704edfafd6962 100644 (file)
@@ -129,8 +129,6 @@ static struct resource osk5912_smc91x_resources[] = {
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
-               .start  = OMAP_GPIO_IRQ(0),
-               .end    = OMAP_GPIO_IRQ(0),
                .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
        },
 };
@@ -147,8 +145,6 @@ static struct platform_device osk5912_smc91x_device = {
 
 static struct resource osk5912_cf_resources[] = {
        [0] = {
-               .start  = OMAP_GPIO_IRQ(62),
-               .end    = OMAP_GPIO_IRQ(62),
                .flags  = IORESOURCE_IRQ,
        },
 };
@@ -240,7 +236,6 @@ static struct tps65010_board tps_board = {
 static struct i2c_board_info __initdata osk_i2c_board_info[] = {
        {
                I2C_BOARD_INFO("tps65010", 0x48),
-               .irq            = OMAP_GPIO_IRQ(OMAP_MPUIO(1)),
                .platform_data  = &tps_board,
 
        },
@@ -408,7 +403,6 @@ static struct spi_board_info __initdata mistral_boardinfo[] = { {
        /* MicroWire (bus 2) CS0 has an ads7846e */
        .modalias               = "ads7846",
        .platform_data          = &mistral_ts_info,
-       .irq                    = OMAP_GPIO_IRQ(4),
        .max_speed_hz           = 120000 /* max sample rate at 3V */
                                        * 26 /* command + data + overhead */,
        .bus_num                = 2,
@@ -471,6 +465,7 @@ static void __init osk_mistral_init(void)
        gpio_direction_input(4);
        irq_set_irq_type(gpio_to_irq(4), IRQ_TYPE_EDGE_FALLING);
 
+       mistral_boardinfo[0].irq = gpio_to_irq(4);
        spi_register_board_info(mistral_boardinfo,
                        ARRAY_SIZE(mistral_boardinfo));
 
@@ -542,6 +537,10 @@ static void __init osk_init(void)
 
        osk_flash_resource.end = osk_flash_resource.start = omap_cs3_phys();
        osk_flash_resource.end += SZ_32M - 1;
+       osk5912_smc91x_resources[1].start = gpio_to_irq(0);
+       osk5912_smc91x_resources[1].end = gpio_to_irq(0);
+       osk5912_cf_resources[0].start = gpio_to_irq(62);
+       osk5912_cf_resources[0].end = gpio_to_irq(62);
        platform_add_devices(osk5912_devices, ARRAY_SIZE(osk5912_devices));
 
        l = omap_readl(USB_TRANSCEIVER_CTRL);
@@ -556,6 +555,7 @@ static void __init osk_init(void)
                gpio_direction_input(OMAP_MPUIO(1));
 
        omap_serial_init();
+       osk_i2c_board_info[0].irq = gpio_to_irq(OMAP_MPUIO(1));
        omap_register_i2c_bus(1, 400, osk_i2c_board_info,
                              ARRAY_SIZE(osk_i2c_board_info));
        osk_mistral_init();
index 0863d8e2bdf147ed70467bb8a7fb5748e5b6c701..a60e6c22f8169ed9027e26b2425b54875d23deba 100644 (file)
@@ -217,7 +217,6 @@ static struct spi_board_info palmte_spi_info[] __initdata = {
                .modalias       = "tsc2102",
                .bus_num        = 2,    /* uWire (officially) */
                .chip_select    = 0,    /* As opposed to 3 */
-               .irq            = OMAP_GPIO_IRQ(PALMTE_PINTDAV_GPIO),
                .max_speed_hz   = 8000000,
        },
 };
@@ -251,6 +250,7 @@ static void __init omap_palmte_init(void)
 
        platform_add_devices(palmte_devices, ARRAY_SIZE(palmte_devices));
 
+       palmte_spi_info[0].irq = gpio_to_irq(PALMTE_PINTDAV_GPIO);
        spi_register_board_info(palmte_spi_info, ARRAY_SIZE(palmte_spi_info));
        palmte_misc_gpio_setup();
        omap_serial_init();
index 4ff699c509c0ebbce98a5c600e067e3a50ecc54b..8d854878547be0fcdcc77e9e539b3d819d33ae97 100644 (file)
@@ -257,7 +257,6 @@ static struct spi_board_info __initdata palmtt_boardinfo[] = {
                /* MicroWire (bus 2) CS0 has an ads7846e */
                .modalias       = "ads7846",
                .platform_data  = &palmtt_ts_info,
-               .irq            = OMAP_GPIO_IRQ(6),
                .max_speed_hz   = 120000        /* max sample rate at 3V */
                                        * 26    /* command + data + overhead */,
                .bus_num        = 2,
@@ -298,6 +297,7 @@ static void __init omap_palmtt_init(void)
 
        platform_add_devices(palmtt_devices, ARRAY_SIZE(palmtt_devices));
 
+       palmtt_boardinfo[0].irq = gpio_to_irq(6);
        spi_register_board_info(palmtt_boardinfo,ARRAY_SIZE(palmtt_boardinfo));
        omap_serial_init();
        omap1_usb_init(&palmtt_usb_config);
index abcbbd339aeb1df2baed04bbc34e8df47ed05cc8..a2c5abcd7c84e56d53c441059cf526294883c294 100644 (file)
@@ -224,7 +224,6 @@ static struct spi_board_info __initdata palmz71_boardinfo[] = { {
        /* MicroWire (bus 2) CS0 has an ads7846e */
        .modalias       = "ads7846",
        .platform_data  = &palmz71_ts_info,
-       .irq            = OMAP_GPIO_IRQ(PALMZ71_PENIRQ_GPIO),
        .max_speed_hz   = 120000        /* max sample rate at 3V */
                                * 26    /* command + data + overhead */,
        .bus_num        = 2,
@@ -313,6 +312,7 @@ omap_palmz71_init(void)
 
        platform_add_devices(devices, ARRAY_SIZE(devices));
 
+       palmz71_boardinfo[0].irq = gpio_to_irq(PALMZ71_PENIRQ_GPIO);
        spi_register_board_info(palmz71_boardinfo,
                                ARRAY_SIZE(palmz71_boardinfo));
        omap1_usb_init(&palmz71_usb_config);
index 659d0f75de2c9e7965d0983e27a20eb833a7363f..37232d04233ff779bb44e631d4675559ef34de19 100644 (file)
@@ -44,7 +44,6 @@
 static struct plat_serial8250_port voiceblue_ports[] = {
        {
                .mapbase        = (unsigned long)(OMAP_CS1_PHYS + 0x40000),
-               .irq            = OMAP_GPIO_IRQ(12),
                .flags          = UPF_BOOT_AUTOCONF | UPF_IOREMAP,
                .iotype         = UPIO_MEM,
                .regshift       = 1,
@@ -52,7 +51,6 @@ static struct plat_serial8250_port voiceblue_ports[] = {
        },
        {
                .mapbase        = (unsigned long)(OMAP_CS1_PHYS + 0x50000),
-               .irq            = OMAP_GPIO_IRQ(13),
                .flags          = UPF_BOOT_AUTOCONF | UPF_IOREMAP,
                .iotype         = UPIO_MEM,
                .regshift       = 1,
@@ -60,7 +58,6 @@ static struct plat_serial8250_port voiceblue_ports[] = {
        },
        {
                .mapbase        = (unsigned long)(OMAP_CS1_PHYS + 0x60000),
-               .irq            = OMAP_GPIO_IRQ(14),
                .flags          = UPF_BOOT_AUTOCONF | UPF_IOREMAP,
                .iotype         = UPIO_MEM,
                .regshift       = 1,
@@ -68,7 +65,6 @@ static struct plat_serial8250_port voiceblue_ports[] = {
        },
        {
                .mapbase        = (unsigned long)(OMAP_CS1_PHYS + 0x70000),
-               .irq            = OMAP_GPIO_IRQ(15),
                .flags          = UPF_BOOT_AUTOCONF | UPF_IOREMAP,
                .iotype         = UPIO_MEM,
                .regshift       = 1,
@@ -80,9 +76,6 @@ static struct plat_serial8250_port voiceblue_ports[] = {
 static struct platform_device serial_device = {
        .name                   = "serial8250",
        .id                     = PLAT8250_DEV_PLATFORM1,
-       .dev                    = {
-               .platform_data  = voiceblue_ports,
-       },
 };
 
 static int __init ext_uart_init(void)
@@ -90,6 +83,11 @@ static int __init ext_uart_init(void)
        if (!machine_is_voiceblue())
                return -ENODEV;
 
+       voiceblue_ports[0].irq = gpio_to_irq(12);
+       voiceblue_ports[1].irq = gpio_to_irq(13);
+       voiceblue_ports[2].irq = gpio_to_irq(14);
+       voiceblue_ports[3].irq = gpio_to_irq(15);
+       serial_device.dev.platform_data = voiceblue_ports;
        return platform_device_register(&serial_device);
 }
 arch_initcall(ext_uart_init);
@@ -128,8 +126,6 @@ static struct resource voiceblue_smc91x_resources[] = {
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
-               .start  = OMAP_GPIO_IRQ(8),
-               .end    = OMAP_GPIO_IRQ(8),
                .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
        },
 };
@@ -275,6 +271,8 @@ static void __init voiceblue_init(void)
        irq_set_irq_type(gpio_to_irq(14), IRQ_TYPE_EDGE_RISING);
        irq_set_irq_type(gpio_to_irq(15), IRQ_TYPE_EDGE_RISING);
 
+       voiceblue_smc91x_resources[1].start = gpio_to_irq(8);
+       voiceblue_smc91x_resources[1].end = gpio_to_irq(8);
        platform_add_devices(voiceblue_devices, ARRAY_SIZE(voiceblue_devices));
        omap_board_config = voiceblue_config;
        omap_board_config_size = ARRAY_SIZE(voiceblue_config);
index f9bf78d4fdfb9681dd38f534477335f0617b9206..401eb3c080c2f325de709d805aac2fc9f260c9f2 100644 (file)
 
 void omap1_set_vpp(struct platform_device *pdev, int enable)
 {
-       static int count;
        u32 l;
 
-       if (enable) {
-               if (count++ == 0) {
-                       l = omap_readl(EMIFS_CONFIG);
-                       l |= OMAP_EMIFS_CONFIG_WP;
-                       omap_writel(l, EMIFS_CONFIG);
-               }
-       } else {
-               if (count && (--count == 0)) {
-                       l = omap_readl(EMIFS_CONFIG);
-                       l &= ~OMAP_EMIFS_CONFIG_WP;
-                       omap_writel(l, EMIFS_CONFIG);
-               }
-       }
+       l = omap_readl(EMIFS_CONFIG);
+       if (enable)
+               l |= OMAP_EMIFS_CONFIG_WP;
+       else
+               l &= ~OMAP_EMIFS_CONFIG_WP;
+       omap_writel(l, EMIFS_CONFIG);
 }
index 306beaca14c57ef169feaf0c06651d93b9000d36..f66c32912b22d5bef0c4ca7d590ef5e5657c34ac 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/io.h>
 #include <linux/atomic.h>
 
+#include <asm/system_misc.h>
 #include <asm/irq.h>
 #include <asm/mach/time.h>
 #include <asm/mach/irq.h>
index c8bda62900d8dd87d9d383edf305651ae21bd354..e658f835d0de3da65c24a4ccba0ec8252fa65e18 100644 (file)
@@ -230,12 +230,12 @@ static struct i2c_board_info __initdata sdp2430_i2c1_boardinfo[] = {
        {
                I2C_BOARD_INFO("isp1301_omap", 0x2D),
                .flags = I2C_CLIENT_WAKE,
-               .irq = OMAP_GPIO_IRQ(78),
        },
 };
 
 static int __init omap2430_i2c_init(void)
 {
+       sdp2430_i2c1_boardinfo[0].irq = gpio_to_irq(78);
        omap_register_i2c_bus(1, 100, sdp2430_i2c1_boardinfo,
                        ARRAY_SIZE(sdp2430_i2c1_boardinfo));
        omap_pmic_init(2, 100, "twl4030", INT_24XX_SYS_NIRQ,
index 37dcb1bc025ea05674d3972d831455a462e43c7f..a39fc4bbd2b8ffb9cb8bb06c1c91f1c40e120333 100644 (file)
@@ -907,7 +907,6 @@ static void __init omap4_sdp4430_wifi_mux_init(void)
 }
 
 static struct wl12xx_platform_data omap4_sdp4430_wlan_data __initdata = {
-       .irq = OMAP_GPIO_IRQ(GPIO_WIFI_IRQ),
        .board_ref_clock = WL12XX_REFCLOCK_26,
        .board_tcxo_clock = WL12XX_TCXOCLOCK_26,
 };
@@ -917,6 +916,7 @@ static void __init omap4_sdp4430_wifi_init(void)
        int ret;
 
        omap4_sdp4430_wifi_mux_init();
+       omap4_sdp4430_wlan_data.irq = gpio_to_irq(GPIO_WIFI_IRQ);
        ret = wl12xx_set_platform_data(&omap4_sdp4430_wlan_data);
        if (ret)
                pr_err("Error setting wl12xx data: %d\n", ret);
index ac773829941f487493f4ce0592dfa787592211a9..768ece2e9c3b4bcf14175a7068c801e68575e6d6 100644 (file)
@@ -136,8 +136,6 @@ static struct resource apollon_smc91x_resources[] = {
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
-               .start  = OMAP_GPIO_IRQ(APOLLON_ETHR_GPIO_IRQ),
-               .end    = OMAP_GPIO_IRQ(APOLLON_ETHR_GPIO_IRQ),
                .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
        },
 };
@@ -341,6 +339,8 @@ static void __init omap_apollon_init(void)
         * You have to mux them off in device drivers later on
         * if not needed.
         */
+       apollon_smc91x_resources[1].start = gpio_to_irq(APOLLON_ETHR_GPIO_IRQ);
+       apollon_smc91x_resources[1].end = gpio_to_irq(APOLLON_ETHR_GPIO_IRQ);
        platform_add_devices(apollon_devices, ARRAY_SIZE(apollon_devices));
        omap_serial_init();
        omap_sdrc_init(NULL, NULL);
index 11cd2a8060939e640c3be752c126fc987b08553b..a2010f07de317b68f2715e05847147776b507276 100644 (file)
@@ -411,7 +411,6 @@ static struct resource omap_dm9000_resources[] = {
                .flags          = IORESOURCE_MEM,
        },
        [2] = {
-               .start          = OMAP_GPIO_IRQ(OMAP_DM9000_GPIO_IRQ),
                .flags          = IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
        },
 };
@@ -639,6 +638,7 @@ static void __init devkit8000_init(void)
 
        omap_hsmmc_init(mmc);
        devkit8000_i2c_init();
+       omap_dm9000_resources[2].start = gpio_to_irq(OMAP_DM9000_GPIO_IRQ);
        platform_add_devices(devkit8000_devices,
                        ARRAY_SIZE(devkit8000_devices));
 
index 54af800d143c0e261b4acd6548103c6b3c08634f..0bbbabe28fcc94bc80f9c3851af7a5d5559405ff 100644 (file)
@@ -348,7 +348,6 @@ static struct at24_platform_data m24c01 = {
 static struct i2c_board_info __initdata h4_i2c_board_info[] = {
        {
                I2C_BOARD_INFO("isp1301_omap", 0x2d),
-               .irq            = OMAP_GPIO_IRQ(125),
        },
        {       /* EEPROM on mainboard */
                I2C_BOARD_INFO("24c01", 0x52),
@@ -377,6 +376,7 @@ static void __init omap_h4_init(void)
         */
 
        board_mkp_init();
+       h4_i2c_board_info[0].irq = gpio_to_irq(125);
        i2c_register_board_info(1, h4_i2c_board_info,
                        ARRAY_SIZE(h4_i2c_board_info));
 
index a659e198892beca6dd98ec167b4eee3e02ba77c0..4c90f078abe16fe7a7aba37c31800ae364ca611b 100644 (file)
@@ -487,7 +487,6 @@ static struct platform_device omap3evm_wlan_regulator = {
 };
 
 struct wl12xx_platform_data omap3evm_wlan_data __initdata = {
-       .irq = OMAP_GPIO_IRQ(OMAP3EVM_WLAN_IRQ_GPIO),
        .board_ref_clock = WL12XX_REFCLOCK_38, /* 38.4 MHz */
 };
 #endif
@@ -623,6 +622,7 @@ static void __init omap3_evm_wl12xx_init(void)
        int ret;
 
        /* WL12xx WLAN Init */
+       omap3evm_wlan_data.irq = gpio_to_irq(OMAP3EVM_WLAN_IRQ_GPIO);
        ret = wl12xx_set_platform_data(&omap3evm_wlan_data);
        if (ret)
                pr_err("error setting wl12xx data: %d\n", ret);
index 8bf8e99c358e494b235d3460954dad789e1b2754..d8c0e89f0126a2b4a7ba2a87d49641d1cf05c57c 100644 (file)
@@ -231,7 +231,6 @@ static struct platform_device omap_vwlan_device = {
 };
 
 struct wl12xx_platform_data omap_panda_wlan_data  __initdata = {
-       .irq = OMAP_GPIO_IRQ(GPIO_WIFI_IRQ),
        /* PANDA ref clock is 38.4 MHz */
        .board_ref_clock = 2,
 };
@@ -558,6 +557,7 @@ static void __init omap4_panda_init(void)
                package = OMAP_PACKAGE_CBL;
        omap4_mux_init(board_mux, NULL, package);
 
+       omap_panda_wlan_data.irq = gpio_to_irq(GPIO_WIFI_IRQ);
        ret = wl12xx_set_platform_data(&omap_panda_wlan_data);
        if (ret)
                pr_err("error setting wl12xx data: %d\n", ret);
index f120997309af261a0bb9c68e1d8eb57ae6e09bbf..d87ee0612098fb82ba350d591adb9281e588097a 100644 (file)
@@ -170,7 +170,6 @@ static struct spi_board_info rx51_peripherals_spi_board_info[] __initdata = {
                .modalias               = "tsc2005",
                .bus_num                = 1,
                .chip_select            = 0,
-               .irq                    = OMAP_GPIO_IRQ(RX51_TSC2005_IRQ_GPIO),
                .max_speed_hz           = 6000000,
                .controller_data        = &tsc2005_mcspi_config,
                .platform_data          = &tsc2005_pdata,
@@ -1129,6 +1128,8 @@ static void __init rx51_init_tsc2005(void)
        }
 
        tsc2005_pdata.set_reset = rx51_tsc2005_set_reset;
+       rx51_peripherals_spi_board_info[RX51_SPI_TSC2005].irq =
+                               gpio_to_irq(RX51_TSC2005_IRQ_GPIO);
 }
 
 void __init rx51_peripherals_init(void)
index 369c2eb7715b30de41323fb4dceb04c1b957c2f0..1e8540eabde98188ced71d321d7b531a685b463a 100644 (file)
@@ -43,7 +43,6 @@ static inline void __init zoom_init_smsc911x(void)
 static struct plat_serial8250_port serial_platform_data[] = {
        {
                .mapbase        = ZOOM_UART_BASE,
-               .irq            = OMAP_GPIO_IRQ(102),
                .flags          = UPF_BOOT_AUTOCONF|UPF_IOREMAP|UPF_SHARE_IRQ,
                .irqflags       = IRQF_SHARED | IRQF_TRIGGER_RISING,
                .iotype         = UPIO_MEM,
@@ -89,6 +88,8 @@ static inline void __init zoom_init_quaduart(void)
        if (gpio_request_one(quart_gpio, GPIOF_IN, "TL16CP754C GPIO") < 0)
                printk(KERN_ERR "Failed to request GPIO%d for TL16CP754C\n",
                                                                quart_gpio);
+
+       serial_platform_data[0].irq = gpio_to_irq(102);
 }
 
 static inline int omap_zoom_debugboard_detect(void)
index 3d39cdb2e25021704b511ca3c6b45dce606243b0..b797cb279618c59428e0a443c5c962c1655fbc5c 100644 (file)
@@ -193,7 +193,6 @@ static struct platform_device omap_vwlan_device = {
 };
 
 static struct wl12xx_platform_data omap_zoom_wlan_data __initdata = {
-       .irq = OMAP_GPIO_IRQ(OMAP_ZOOM_WLAN_IRQ_GPIO),
        /* ZOOM ref clock is 26 MHz */
        .board_ref_clock = 1,
 };
@@ -297,7 +296,10 @@ static void enable_board_wakeup_source(void)
 
 void __init zoom_peripherals_init(void)
 {
-       int ret = wl12xx_set_platform_data(&omap_zoom_wlan_data);
+       int ret;
+
+       omap_zoom_wlan_data.irq = gpio_to_irq(OMAP_ZOOM_WLAN_IRQ_GPIO);
+       ret = wl12xx_set_platform_data(&omap_zoom_wlan_data);
 
        if (ret)
                pr_err("error setting wl12xx data: %d\n", ret);
index 9498b0f5fbd081403d88569bfc314a331f9f3cd9..1706ebcec08d79c08ea23191b949266613c9406e 100644 (file)
@@ -76,7 +76,7 @@ void __init omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce,
        }
 
        spi_bi->bus_num = bus_num;
-       spi_bi->irq     = OMAP_GPIO_IRQ(gpio_pendown);
+       spi_bi->irq     = gpio_to_irq(gpio_pendown);
 
        if (board_pdata) {
                board_pdata->gpio_pendown = gpio_pendown;
index 464cffde58fe9ecb5c6b26af3f4409b9d34506f3..535866489ce3ad3cc0685c1fde3e5d43e09d7fd9 100644 (file)
@@ -87,29 +87,14 @@ static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
        return 0;
 }
 
-/**
- * omap3_enter_idle - Programs OMAP3 to enter the specified state
- * @dev: cpuidle device
- * @drv: cpuidle driver
- * @index: the index of state to be entered
- *
- * Called from the CPUidle framework to program the device to the
- * specified target state selected by the governor.
- */
-static int omap3_enter_idle(struct cpuidle_device *dev,
+static int __omap3_enter_idle(struct cpuidle_device *dev,
                                struct cpuidle_driver *drv,
                                int index)
 {
        struct omap3_idle_statedata *cx =
                        cpuidle_get_statedata(&dev->states_usage[index]);
-       struct timespec ts_preidle, ts_postidle, ts_idle;
        u32 mpu_state = cx->mpu_state, core_state = cx->core_state;
-       int idle_time;
-
-       /* Used to keep track of the total time in idle */
-       getnstimeofday(&ts_preidle);
 
-       local_irq_disable();
        local_fiq_disable();
 
        pwrdm_set_next_pwrst(mpu_pd, mpu_state);
@@ -148,21 +133,28 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
        }
 
 return_sleep_time:
-       getnstimeofday(&ts_postidle);
-       ts_idle = timespec_sub(ts_postidle, ts_preidle);
 
-       local_irq_enable();
        local_fiq_enable();
 
-       idle_time = ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * \
-                                                               USEC_PER_SEC;
-
-       /* Update cpuidle counters */
-       dev->last_residency = idle_time;
-
        return index;
 }
 
+/**
+ * omap3_enter_idle - Programs OMAP3 to enter the specified state
+ * @dev: cpuidle device
+ * @drv: cpuidle driver
+ * @index: the index of state to be entered
+ *
+ * Called from the CPUidle framework to program the device to the
+ * specified target state selected by the governor.
+ */
+static inline int omap3_enter_idle(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv,
+                               int index)
+{
+       return cpuidle_wrap_enter(dev, drv, index, __omap3_enter_idle);
+}
+
 /**
  * next_valid_state - Find next valid C-state
  * @dev: cpuidle device
index 72e018b9b260db422108f7d70084c7ae8d41ba2b..f386cbe9c889032668058731e56fb116af245436 100644 (file)
@@ -62,15 +62,9 @@ static int omap4_enter_idle(struct cpuidle_device *dev,
 {
        struct omap4_idle_statedata *cx =
                        cpuidle_get_statedata(&dev->states_usage[index]);
-       struct timespec ts_preidle, ts_postidle, ts_idle;
        u32 cpu1_state;
-       int idle_time;
        int cpu_id = smp_processor_id();
 
-       /* Used to keep track of the total time in idle */
-       getnstimeofday(&ts_preidle);
-
-       local_irq_disable();
        local_fiq_disable();
 
        /*
@@ -128,26 +122,17 @@ static int omap4_enter_idle(struct cpuidle_device *dev,
        if (index > 0)
                clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
 
-       getnstimeofday(&ts_postidle);
-       ts_idle = timespec_sub(ts_postidle, ts_preidle);
-
-       local_irq_enable();
        local_fiq_enable();
 
-       idle_time = ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * \
-                                                               USEC_PER_SEC;
-
-       /* Update cpuidle counters */
-       dev->last_residency = idle_time;
-
        return index;
 }
 
 DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev);
 
 struct cpuidle_driver omap4_idle_driver = {
-       .name =         "omap4_idle",
-       .owner =        THIS_MODULE,
+       .name                           = "omap4_idle",
+       .owner                          = THIS_MODULE,
+       .en_core_tk_irqen               = 1,
 };
 
 static inline void _fill_cstate(struct cpuidle_driver *drv,
index 9706c648bc19b53c7f9d7b2abd5d4d411e956fac..db5a88a36c63418d746e6fbfd6ecb89d97e2c2e5 100644 (file)
@@ -99,7 +99,7 @@ static const struct omap_dss_hwmod_data omap4_dss_hwmod_data[] __initdata = {
        { "dss_hdmi", "omapdss_hdmi", -1 },
 };
 
-static void omap4_hdmi_mux_pads(enum omap_hdmi_flags flags)
+static void __init omap4_hdmi_mux_pads(enum omap_hdmi_flags flags)
 {
        u32 reg;
        u16 control_i2c_1;
@@ -125,7 +125,7 @@ static void omap4_hdmi_mux_pads(enum omap_hdmi_flags flags)
        }
 }
 
-static int __init omap4_dsi_mux_pads(int dsi_id, unsigned lanes)
+static int omap4_dsi_mux_pads(int dsi_id, unsigned lanes)
 {
        u32 enable_mask, enable_shift;
        u32 pipd_mask, pipd_shift;
@@ -166,7 +166,7 @@ int __init omap_hdmi_init(enum omap_hdmi_flags flags)
        return 0;
 }
 
-static int __init omap_dsi_enable_pads(int dsi_id, unsigned lane_mask)
+static int omap_dsi_enable_pads(int dsi_id, unsigned lane_mask)
 {
        if (cpu_is_omap44xx())
                return omap4_dsi_mux_pads(dsi_id, lane_mask);
@@ -174,7 +174,7 @@ static int __init omap_dsi_enable_pads(int dsi_id, unsigned lane_mask)
        return 0;
 }
 
-static void __init omap_dsi_disable_pads(int dsi_id, unsigned lane_mask)
+static void omap_dsi_disable_pads(int dsi_id, unsigned lane_mask)
 {
        if (cpu_is_omap44xx())
                omap4_dsi_mux_pads(dsi_id, 0);
index a7bdec69a2b3b45d169fca4176b272599f1c6ec8..d0c1c9695996d43f6ba7aa1de95e2cb58cd31048 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/export.h>
 #include <linux/suspend.h>
 
+#include <asm/system_misc.h>
+
 #include <plat/omap-pm.h>
 #include <plat/omap_device.h>
 #include "common.h"
index 638eebedc883529a8be95937f542af38f919b4b7..31327401627725bbc6fa3a4b3cadfc5426d436cb 100644 (file)
@@ -714,7 +714,6 @@ struct da9030_battery_info cm_x300_battery_info = {
 
 static struct regulator_consumer_supply buck2_consumers[] = {
        {
-               .dev = NULL,
                .supply = "vcc_core",
        },
 };
index c1b65da263358b6cf554728cb23b51e0631ec94c..16ec557b8e43f50bc58014513724d86177e75c09 100644 (file)
@@ -1083,19 +1083,19 @@ static void __init em_x270_userspace_consumers_init(void)
 }
 
 /* DA9030 related initializations */
-#define REGULATOR_CONSUMER(_name, _dev, _supply)                              \
+#define REGULATOR_CONSUMER(_name, _dev_name, _supply)                  \
        static struct regulator_consumer_supply _name##_consumers[] = { \
                {                                                       \
-                       .dev = _dev,                                    \
+                       .dev_name = _dev_name,                          \
                        .supply = _supply,                              \
                },                                                      \
        }
 
-REGULATOR_CONSUMER(ldo3, &em_x270_gps_userspace_consumer.dev, "vcc gps");
+REGULATOR_CONSUMER(ldo3, "reg-userspace-consumer.0", "vcc gps");
 REGULATOR_CONSUMER(ldo5, NULL, "vcc cam");
-REGULATOR_CONSUMER(ldo10, &pxa_device_mci.dev, "vcc sdio");
+REGULATOR_CONSUMER(ldo10, "pxa2xx-mci", "vcc sdio");
 REGULATOR_CONSUMER(ldo12, NULL, "vcc usb");
-REGULATOR_CONSUMER(ldo19, &em_x270_gprs_userspace_consumer.dev, "vcc gprs");
+REGULATOR_CONSUMER(ldo19, "reg-userspace-consumer.1", "vcc gprs");
 REGULATOR_CONSUMER(buck2, NULL, "vcc_core");
 
 #define REGULATOR_INIT(_ldo, _min_uV, _max_uV, _ops_mask)              \
index 3fa929d4a4f5b748ec9885c11d255d33aa00b6cd..b83b95a2950342b298a8693b753217feacb4b5ab 100644 (file)
@@ -681,11 +681,9 @@ static struct platform_device power_supply = {
 
 static struct regulator_consumer_supply bq24022_consumers[] = {
        {
-               .dev = &gpio_vbus.dev,
                .supply = "vbus_draw",
        },
        {
-               .dev = &power_supply.dev,
                .supply = "ac_draw",
        },
 };
index 6f4785b347c25f44af25b49a6786f7ad1cec4e6e..8de0651d7efbdffb3c509814f7ded4bab8149ec6 100644 (file)
@@ -580,11 +580,9 @@ static struct platform_device power_supply = {
 
 static struct regulator_consumer_supply bq24022_consumers[] = {
        {
-               .dev = &gpio_vbus.dev,
                .supply = "vbus_draw",
        },
        {
-               .dev = &power_supply.dev,
                .supply = "ac_draw",
        },
 };
index 7d691e51cb54048d679440b766dba67a7b3c9f4c..5905ed130e94200abe59867d0b916e718cedae98 100644 (file)
@@ -43,6 +43,8 @@
 #include <linux/regulator/consumer.h>
 #include <linux/delay.h>
 
+#include <asm/system_info.h>
+
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
index adb601a3762fb6e2612339fb306b959cd27a722d..4cd645e29b64aa06215f937436dfaa94e52a0258 100644 (file)
@@ -152,7 +152,7 @@ static struct platform_device sht15 = {
 
 static struct regulator_consumer_supply stargate2_sensor_3_con[] = {
        {
-               .dev = &sht15.dev,
+               .dev_name = "sht15",
                .supply = "vcc",
        },
 };
index 2119ca6a73bc9c5f0c43fd52796a3ad662ba4cd6..b9d6d4f92c034c461ee04af9f3632c6342feeb54 100644 (file)
@@ -35,9 +35,7 @@
 static void simtec_nor_vpp(struct platform_device *pdev, int vpp)
 {
        unsigned int val;
-       unsigned long flags;
 
-       local_irq_save(flags);
        val = __raw_readb(BAST_VA_CTRL3);
 
        printk(KERN_DEBUG "%s(%d)\n", __func__, vpp);
@@ -48,7 +46,6 @@ static void simtec_nor_vpp(struct platform_device *pdev, int vpp)
                val &= ~BAST_CPLD_CTRL3_ROMWEN;
 
        __raw_writeb(val, BAST_VA_CTRL3);
-       local_irq_restore(flags);
 }
 
 static struct physmap_flash_data simtec_nor_pdata = {
index 6a2a7f2c255708e80d909c7026e15413a797eba4..2704bcd869cdc1d61843bc85c3a4bd2e5f36d5fc 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/mach-types.h>
 #include <asm/leds.h>
 #include <asm/param.h>
+#include <asm/system_misc.h>
 
 #include <asm/mach/map.h>
 #include <asm/mach/arch.h>
index a125d4e114ec53db0500632fa7d8addaa5d99bee..f49e28abe0abe59d987aec558d357dd78e2ec7f3 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/physmap.h>
+#include <linux/mtd/sh_flctl.h>
 #include <linux/pm_clock.h>
 #include <linux/smsc911x.h>
 #include <linux/sh_intc.h>
@@ -956,6 +957,50 @@ static struct platform_device fsi_ak4643_device = {
        },
 };
 
+/* FLCTL */
+static struct mtd_partition nand_partition_info[] = {
+       {
+               .name   = "system",
+               .offset = 0,
+               .size   = 128 * 1024 * 1024,
+       },
+       {
+               .name   = "userdata",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = 256 * 1024 * 1024,
+       },
+       {
+               .name   = "cache",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = 128 * 1024 * 1024,
+       },
+};
+
+static struct resource nand_flash_resources[] = {
+       [0] = {
+               .start  = 0xe6a30000,
+               .end    = 0xe6a3009b,
+               .flags  = IORESOURCE_MEM,
+       }
+};
+
+static struct sh_flctl_platform_data nand_flash_data = {
+       .parts          = nand_partition_info,
+       .nr_parts       = ARRAY_SIZE(nand_partition_info),
+       .flcmncr_val    = CLK_16B_12L_4H | TYPESEL_SET
+                       | SHBUSSEL | SEL_16BIT | SNAND_E,
+       .use_holden     = 1,
+};
+
+static struct platform_device nand_flash_device = {
+       .name           = "sh_flctl",
+       .resource       = nand_flash_resources,
+       .num_resources  = ARRAY_SIZE(nand_flash_resources),
+       .dev            = {
+               .platform_data = &nand_flash_data,
+       },
+};
+
 /*
  * The card detect pin of the top SD/MMC slot (CN7) is active low and is
  * connected to GPIO A22 of SH7372 (GPIO_PORT41).
@@ -1259,6 +1304,7 @@ static struct platform_device *mackerel_devices[] __initdata = {
        &fsi_device,
        &fsi_ak4643_device,
        &fsi_hdmi_device,
+       &nand_flash_device,
        &sdhi0_device,
 #if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
        &sdhi1_device,
@@ -1488,6 +1534,30 @@ static void __init mackerel_init(void)
        gpio_request(GPIO_FN_MMCCMD0, NULL);
        gpio_request(GPIO_FN_MMCCLK0, NULL);
 
+       /* FLCTL */
+       gpio_request(GPIO_FN_D0_NAF0, NULL);
+       gpio_request(GPIO_FN_D1_NAF1, NULL);
+       gpio_request(GPIO_FN_D2_NAF2, NULL);
+       gpio_request(GPIO_FN_D3_NAF3, NULL);
+       gpio_request(GPIO_FN_D4_NAF4, NULL);
+       gpio_request(GPIO_FN_D5_NAF5, NULL);
+       gpio_request(GPIO_FN_D6_NAF6, NULL);
+       gpio_request(GPIO_FN_D7_NAF7, NULL);
+       gpio_request(GPIO_FN_D8_NAF8, NULL);
+       gpio_request(GPIO_FN_D9_NAF9, NULL);
+       gpio_request(GPIO_FN_D10_NAF10, NULL);
+       gpio_request(GPIO_FN_D11_NAF11, NULL);
+       gpio_request(GPIO_FN_D12_NAF12, NULL);
+       gpio_request(GPIO_FN_D13_NAF13, NULL);
+       gpio_request(GPIO_FN_D14_NAF14, NULL);
+       gpio_request(GPIO_FN_D15_NAF15, NULL);
+       gpio_request(GPIO_FN_FCE0, NULL);
+       gpio_request(GPIO_FN_WE0_FWE, NULL);
+       gpio_request(GPIO_FN_FRB, NULL);
+       gpio_request(GPIO_FN_A4_FOE, NULL);
+       gpio_request(GPIO_FN_A5_FCDE, NULL);
+       gpio_request(GPIO_FN_RD_FSC, NULL);
+
        /* enable GPS module (GT-720F) */
        gpio_request(GPIO_FN_SCIFA2_TXD1, NULL);
        gpio_request(GPIO_FN_SCIFA2_RXD1, NULL);
@@ -1532,6 +1602,7 @@ static void __init mackerel_init(void)
        sh7372_add_device_to_domain(&sh7372_a4mp, &fsi_device);
        sh7372_add_device_to_domain(&sh7372_a3sp, &usbhs0_device);
        sh7372_add_device_to_domain(&sh7372_a3sp, &usbhs1_device);
+       sh7372_add_device_to_domain(&sh7372_a3sp, &nand_flash_device);
        sh7372_add_device_to_domain(&sh7372_a3sp, &sh_mmcif_device);
        sh7372_add_device_to_domain(&sh7372_a3sp, &sdhi0_device);
 #if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
index de243e3c83929311753e47f0dc1dc1d495e88761..94d1f88246d3fc780267ad698b9d43e73c339eaa 100644 (file)
@@ -511,7 +511,7 @@ enum { MSTP001, MSTP000,
        MSTP223,
        MSTP218, MSTP217, MSTP216, MSTP214, MSTP208, MSTP207,
        MSTP206, MSTP205, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
-       MSTP328, MSTP323, MSTP322, MSTP314, MSTP313, MSTP312,
+       MSTP328, MSTP323, MSTP322, MSTP315, MSTP314, MSTP313, MSTP312,
        MSTP423, MSTP415, MSTP413, MSTP411, MSTP410, MSTP407, MSTP406,
        MSTP405, MSTP404, MSTP403, MSTP400,
        MSTP_NR };
@@ -553,6 +553,7 @@ static struct clk mstp_clks[MSTP_NR] = {
        [MSTP328] = MSTP(&div6_clks[DIV6_SPU], SMSTPCR3, 28, 0), /* FSI2 */
        [MSTP323] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 23, 0), /* IIC1 */
        [MSTP322] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 22, 0), /* USB0 */
+       [MSTP315] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 15, 0), /* FLCTL*/
        [MSTP314] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 14, 0), /* SDHI0 */
        [MSTP313] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 13, 0), /* SDHI1 */
        [MSTP312] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 12, 0), /* MMC */
@@ -653,6 +654,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("r8a66597_hcd.0", &mstp_clks[MSTP322]), /* USB0 */
        CLKDEV_DEV_ID("r8a66597_udc.0", &mstp_clks[MSTP322]), /* USB0 */
        CLKDEV_DEV_ID("renesas_usbhs.0", &mstp_clks[MSTP322]), /* USB0 */
+       CLKDEV_DEV_ID("sh_flctl.0", &mstp_clks[MSTP315]), /* FLCTL */
        CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]), /* SDHI0 */
        CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), /* SDHI1 */
        CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP312]), /* MMC */
index 21b09b6455e4b7788057df17d624f3778a7cad21..7e6559105d40f27be8ffbdbc394cc377cec65ade 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/suspend.h>
 #include <linux/module.h>
 #include <linux/err.h>
+#include <asm/cpuidle.h>
 #include <asm/io.h>
 
 static void shmobile_enter_wfi(void)
@@ -28,37 +29,19 @@ static int shmobile_cpuidle_enter(struct cpuidle_device *dev,
                                  struct cpuidle_driver *drv,
                                  int index)
 {
-       ktime_t before, after;
-
-       before = ktime_get();
-
-       local_irq_disable();
-       local_fiq_disable();
-
        shmobile_cpuidle_modes[index]();
 
-       local_irq_enable();
-       local_fiq_enable();
-
-       after = ktime_get();
-       dev->last_residency = ktime_to_ns(ktime_sub(after, before)) >> 10;
-
        return index;
 }
 
 static struct cpuidle_device shmobile_cpuidle_dev;
 static struct cpuidle_driver shmobile_cpuidle_driver = {
-       .name =         "shmobile_cpuidle",
-       .owner =        THIS_MODULE,
-       .states[0] = {
-               .name = "C1",
-               .desc = "WFI",
-               .exit_latency = 1,
-               .target_residency = 1 * 2,
-               .flags = CPUIDLE_FLAG_TIME_VALID,
-       },
-       .safe_state_index = 0, /* C1 */
-       .state_count = 1,
+       .name                   = "shmobile_cpuidle",
+       .owner                  = THIS_MODULE,
+       .en_core_tk_irqen       = 1,
+       .states[0]              = ARM_CPUIDLE_WFI_STATE,
+       .safe_state_index       = 0, /* C1 */
+       .state_count            = 1,
 };
 
 void (*shmobile_cpuidle_setup)(struct cpuidle_driver *drv);
index e20b419d598342f55453f3526969e34120728517..0952494f481aa4a7c5dd4f7f6e615b9148f218c1 100644 (file)
@@ -68,11 +68,11 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
        OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S2_BASE, "tegra-i2s.1", NULL),
        OF_DEV_AUXDATA("nvidia,tegra20-das", TEGRA_APB_MISC_DAS_BASE, "tegra-das", NULL),
        OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB_BASE, "tegra-ehci.0",
-                      &tegra_ehci1_device.dev.platform_data),
+                      &tegra_ehci1_pdata),
        OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB2_BASE, "tegra-ehci.1",
-                      &tegra_ehci2_device.dev.platform_data),
+                      &tegra_ehci2_pdata),
        OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB3_BASE, "tegra-ehci.2",
-                      &tegra_ehci3_device.dev.platform_data),
+                      &tegra_ehci3_pdata),
        {}
 };
 
index 7a2a02dbd632f3134ca98730497368b0d555fc9d..5f6b867e20b49c77c2635017e44b6dc0d8eeafe1 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/fsl_devices.h>
 #include <linux/serial_8250.h>
 #include <linux/i2c-tegra.h>
-#include <linux/platform_data/tegra_usb.h>
 #include <asm/pmu.h>
 #include <mach/irqs.h>
 #include <mach/iomap.h>
@@ -446,18 +445,18 @@ static struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = {
        .clk = "cdev2",
 };
 
-static struct tegra_ehci_platform_data tegra_ehci1_pdata = {
+struct tegra_ehci_platform_data tegra_ehci1_pdata = {
        .operating_mode = TEGRA_USB_OTG,
        .power_down_on_bus_suspend = 1,
 };
 
-static struct tegra_ehci_platform_data tegra_ehci2_pdata = {
+struct tegra_ehci_platform_data tegra_ehci2_pdata = {
        .phy_config = &tegra_ehci2_ulpi_phy_config,
        .operating_mode = TEGRA_USB_HOST,
        .power_down_on_bus_suspend = 1,
 };
 
-static struct tegra_ehci_platform_data tegra_ehci3_pdata = {
+struct tegra_ehci_platform_data tegra_ehci3_pdata = {
        .operating_mode = TEGRA_USB_HOST,
        .power_down_on_bus_suspend = 1,
 };
index 873ecb2f8ae6af0fb40111457e2c1ae7c1a06ed6..ec455679b219a987dcade1063f7770559746e9f4 100644 (file)
 #define __MACH_TEGRA_DEVICES_H
 
 #include <linux/platform_device.h>
+#include <linux/platform_data/tegra_usb.h>
+
+extern struct tegra_ehci_platform_data tegra_ehci1_pdata;
+extern struct tegra_ehci_platform_data tegra_ehci2_pdata;
+extern struct tegra_ehci_platform_data tegra_ehci3_pdata;
 
 extern struct platform_device tegra_gpio_device;
 extern struct platform_device tegra_pinmux_device;
index 8b90c44d237f92d2afcb261cf698500979842fea..1621ad07d284fea97626fea99a3e5535844c8417 100644 (file)
@@ -1544,6 +1544,8 @@ static struct fsmc_nand_platform_data nand_platform_data = {
        .nr_partitions = ARRAY_SIZE(u300_partitions),
        .options = NAND_SKIP_BBTSCAN,
        .width = FSMC_NAND_BW8,
+       .ale_off = PLAT_NAND_ALE,
+       .cle_off = PLAT_NAND_CLE,
 };
 
 static struct platform_device nand_device = {
index 7b7cba960b690945fbd9830e893eb3c27ade58c8..65f87c523892e0826ea65d4928cb14cb10b30349 100644 (file)
 /* NFIF */
 #define U300_NAND_IF_PHYS_BASE         0x9f800000
 
+/* ALE, CLE offset for FSMC NAND */
+#define PLAT_NAND_CLE                  (1 << 16)
+#define PLAT_NAND_ALE                  (1 << 17)
+
+
 /* AHB Peripherals */
 #define U300_AHB_PER_PHYS_BASE         0xa0000000
 #define U300_AHB_PER_VIRT_BASE         0xff010000
index db82568a998a15b39db31a8813d92d7cad730ddd..48f5b9fdfb7fe7624c710e0b3e57400edcf1a1cc 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
+#include <asm/system_misc.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <asm/mach/irq.h>
index b8a96c6a1a30773d6231f3fe100539168a7bc2ca..2f6e9924a814796db36d92d383e42de7aa4b5be3 100644 (file)
 #define OMAP_MPUIO(nr)         (OMAP_MAX_GPIO_LINES + (nr))
 #define OMAP_GPIO_IS_MPUIO(nr) ((nr) >= OMAP_MAX_GPIO_LINES)
 
-#define OMAP_GPIO_IRQ(nr)      (OMAP_GPIO_IS_MPUIO(nr) ? \
-                                IH_MPUIO_BASE + ((nr) & 0x0f) : \
-                                IH_GPIO_BASE + (nr))
-
 struct omap_gpio_dev_attr {
        int bank_width;         /* GPIO bank width */
        bool dbck_flag;         /* dbck required or not - True for OMAP3&4 */
index 0db73ae646bcfb616242b04340eb528ca70551bb..290942d9adda6a1557e3cb6b7b12c98bd2795cb1 100644 (file)
@@ -36,6 +36,7 @@
 #include <asm/irq.h>
 #include <asm/cacheflush.h>
 #include <asm/system_info.h>
+#include <asm/system_misc.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index c16cc31ecbed6fcb1c28e3a1b2e6f51e16df9bca..0562f134621df7c2320987cabdc3529c35e56c71 100644 (file)
@@ -159,11 +159,4 @@ struct kbd_platform_data {
        unsigned int mode;
 };
 
-/* This function is used to set platform data field of pdev->dev */
-static inline void
-kbd_set_plat_data(struct platform_device *pdev, struct kbd_platform_data *data)
-{
-       pdev->dev.platform_data = data;
-}
-
 #endif /* __PLAT_KEYBOARD_H */
index 1848bf0d7f62642568356ded4833102e91724216..2a3b53978a3b233bce146b15cb50549269b238fe 100644 (file)
@@ -6,8 +6,6 @@
 # for more details.
 #
 
-MKIMAGE                := $(srctree)/scripts/mkuboot.sh
-
 extra-y                := vmlinux.bin vmlinux.gz
 
 OBJCOPYFLAGS_vmlinux.bin := -O binary -R .note.gnu.build-id
@@ -17,10 +15,9 @@ $(obj)/vmlinux.bin: vmlinux FORCE
 $(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE
        $(call if_changed,gzip)
 
-quiet_cmd_uimage = UIMAGE $@
-      cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A avr32 -O linux -T kernel      \
-               -C gzip -a $(CONFIG_LOAD_ADDRESS) -e $(CONFIG_ENTRY_ADDRESS)    \
-               -n 'Linux-$(KERNELRELEASE)' -d $< $@
+UIMAGE_LOADADDR = $(CONFIG_LOAD_ADDRESS)
+UIMAGE_ENTRYADDR = $(CONFIG_ENTRY_ADDRESS)
+UIMAGE_COMPRESSION = gzip
 
 targets += uImage uImage.srec
 $(obj)/uImage: $(obj)/vmlinux.gz
index 0a49279e3428418f9cb1e04ada32e8371620139b..f7d27d50d02c83fa35ad4606c11ecbd13a555904 100644 (file)
@@ -6,20 +6,17 @@
 # for more details.
 #
 
-MKIMAGE := $(srctree)/scripts/mkuboot.sh
-
 targets := vmImage vmImage.bin vmImage.bz2 vmImage.gz vmImage.lzma vmImage.lzo vmImage.xip
 extra-y += vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma vmlinux.bin.lzo vmlinux.bin.xip
 
-UIMAGE_OPTS-y :=
-UIMAGE_OPTS-$(CONFIG_RAMKERNEL) += -a $(CONFIG_BOOT_LOAD)
-UIMAGE_OPTS-$(CONFIG_ROMKERNEL) += -a $(CONFIG_ROM_BASE) -x
-
-quiet_cmd_uimage = UIMAGE  $@
-      cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A $(ARCH) -O linux -T kernel \
-                   -C $(2) -n '$(CPU_REV)-$(KERNELRELEASE)' \
-                   -e $(shell $(NM) vmlinux | awk '$$NF == "__start" {print $$1}') \
-                   $(UIMAGE_OPTS-y) -d $< $@
+ifeq ($(CONFIG_RAMKERNEL),y)
+UIMAGE_LOADADDR = $(CONFIG_BOOT_LOAD)
+else # CONFIG_ROMKERNEL must be set
+UIMAGE_LOADADDR = $(CONFIG_ROM_BASE)
+endif
+UIMAGE_ENTRYADDR = $(shell $(NM) vmlinux | awk '$$NF == "__start" {print $$1}')
+UIMAGE_NAME = '$(CPU_REV)-$(KERNELRELEASE)'
+UIMAGE_OPTS-$(CONFIG_ROMKERNEL) += -x
 
 $(obj)/vmlinux.bin: vmlinux FORCE
        $(call if_changed,objcopy)
index 507c66c77600792d9aa51250419a8210906a8d19..4c96187e204908bb3a9ba7e37a198df61159ff28 100644 (file)
@@ -1 +1 @@
-/* Future home of xchg() and cmpxchg() */
+#include <asm/intrinsics.h>
index 34940c828def82fc33d71e16827338698f8cd3e6..fa83ea497db75574b04beb6fc349f8d8d5f575a9 100644 (file)
@@ -2,8 +2,6 @@
 # arch/microblaze/boot/Makefile
 #
 
-MKIMAGE := $(srctree)/scripts/mkuboot.sh
-
 obj-y += linked_dtb.o
 
 targets := linux.bin linux.bin.gz simpleImage.%
@@ -35,11 +33,9 @@ quiet_cmd_strip = STRIP   $@
        cmd_strip = $(STRIP) -K microblaze_start -K _end -K __log_buf \
                                -K _fdt_start vmlinux -o $@
 
-quiet_cmd_uimage = UIMAGE  $@.ub
-       cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A microblaze -O linux -T kernel \
-               -C none -n 'Linux-$(KERNELRELEASE)' \
-               -a $(CONFIG_KERNEL_BASE_ADDR) -e $(CONFIG_KERNEL_BASE_ADDR) \
-               -d $@ $@.ub
+UIMAGE_IN = $@
+UIMAGE_OUT = $@.ub
+UIMAGE_LOADADDR = $(CONFIG_KERNEL_BASE_ADDR)
 
 $(obj)/simpleImage.%: vmlinux FORCE
        $(call if_changed,cp,.unstrip)
index 0a430e06f5e5e11f3989265a1f49336d1b10abee..e44a55bc7f0dc13c5901a47ddfb00c5c6ed8f6d5 100644 (file)
@@ -60,7 +60,7 @@ static int __init flash_init(void)
                if (mymtd) {
                        mymtd->owner = THIS_MODULE;
                        mtd_device_parse_register(mymtd, part_probe_types,
-                                                 0, NULL, 0);
+                                                 NULL, NULL, 0);
                } else {
                        pr_err("Failed to register MTD device for flash\n");
                }
index 2388bdb3283283870016fc03448e8ac569011d57..49df14805a9b44bba6857ec4138fe62164e935ab 100644 (file)
@@ -8,6 +8,29 @@
 #include <asm/atomic.h>
 #include <asm/errno.h>
 
+/* The following has to match the LWS code in syscall.S.  We have
+   sixteen four-word locks. */
+
+static inline void
+_futex_spin_lock_irqsave(u32 __user *uaddr, unsigned long int *flags)
+{
+       extern u32 lws_lock_start[];
+       long index = ((long)uaddr & 0xf0) >> 2;
+       arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
+       local_irq_save(*flags);
+       arch_spin_lock(s);
+}
+
+static inline void
+_futex_spin_unlock_irqrestore(u32 __user *uaddr, unsigned long int *flags)
+{
+       extern u32 lws_lock_start[];
+       long index = ((long)uaddr & 0xf0) >> 2;
+       arch_spinlock_t *s = (arch_spinlock_t *)&lws_lock_start[index];
+       arch_spin_unlock(s);
+       local_irq_restore(*flags);
+}
+
 static inline int
 futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
 {
@@ -26,7 +49,7 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
 
        pagefault_disable();
 
-       _atomic_spin_lock_irqsave(uaddr, flags);
+       _futex_spin_lock_irqsave(uaddr, &flags);
 
        switch (op) {
        case FUTEX_OP_SET:
@@ -71,7 +94,7 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
                ret = -ENOSYS;
        }
 
-       _atomic_spin_unlock_irqrestore(uaddr, flags);
+       _futex_spin_unlock_irqrestore(uaddr, &flags);
 
        pagefault_enable();
 
@@ -113,7 +136,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
         * address. This should scale to a couple of CPUs.
         */
 
-       _atomic_spin_lock_irqsave(uaddr, flags);
+       _futex_spin_lock_irqsave(uaddr, &flags);
 
        ret = get_user(val, uaddr);
 
@@ -122,7 +145,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
 
        *uval = val;
 
-       _atomic_spin_unlock_irqrestore(uaddr, flags);
+       _futex_spin_unlock_irqrestore(uaddr, &flags);
 
        return ret;
 }
index 5006e8ea305183eb5998d6f273cd3ad15dd2df7c..0bb1d63907f88a4ac915c2398b9a154744d91265 100644 (file)
@@ -290,8 +290,7 @@ smp_cpu_init(int cpunum)
        mb();
 
        /* Well, support 2.4 linux scheme as well. */
-       if (cpu_isset(cpunum, cpu_online_map))
-       {
+       if (cpu_online(cpunum)) {
                extern void machine_halt(void); /* arch/parisc.../process.c */
 
                printk(KERN_CRIT "CPU#%d already initialized!\n", cpunum);
index e49db5d5d06f2342fe8b60f0071774c4963cb323..a3afecdae14599bfe0e77a65a19f66eb7e26581b 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef _ASM_S390_CPU_MF_H
 #define _ASM_S390_CPU_MF_H
 
+#include <asm/facility.h>
+
 #define CPU_MF_INT_SF_IAE      (1 << 31)       /* invalid entry address */
 #define CPU_MF_INT_SF_ISE      (1 << 30)       /* incorrect SDBT entry */
 #define CPU_MF_INT_SF_PRA      (1 << 29)       /* program request alert */
index 1c7d6ce328bf6bfe52cf715ac8680c2143a2dc76..6340178748bf470bcb724eaa1e7feb066e261884 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __MMU_H
 #define __MMU_H
 
+#include <linux/errno.h>
+
 typedef struct {
        atomic_t attach_count;
        unsigned int flush_mm;
index ac39e7a731fc47542e7e36bc81ae557b1dddaf98..87f080b17af103f1aa6d2977c9a7266ae87d3ed8 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/timer.h>
 #include <linux/slab.h>
+#include <asm/facility.h>
 #include <asm/sysinfo.h>
 #include <asm/ebcdic.h>
 #include <asm/debug.h>
index 8481ecf2ad714e5a71d69cef47b752c27a48c0cd..46405086479c246f6439151c879ab7430d59e5f2 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/notifier.h>
 #include <linux/init.h>
 #include <linux/export.h>
-#include <asm/system.h>
+#include <asm/ctl_reg.h>
 #include <asm/irq.h>
 #include <asm/cpu_mf.h>
 
index 609f985198cfce46227e769987d6b4ac06d2c392..f58f37f66824492c09acfc0c54c59c170be3e2a8 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/perf_event.h>
 #include <linux/percpu.h>
 #include <linux/export.h>
-#include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/cpu_mf.h>
 #include <asm/lowcore.h>
index 1581ea2e027a02edd69000d1edd9cd98b29730d2..06264ae8ccd9e05fd54f166d4d2bdaa37df18441 100644 (file)
@@ -50,6 +50,7 @@
 
 #include <asm/ipl.h>
 #include <asm/uaccess.h>
+#include <asm/facility.h>
 #include <asm/smp.h>
 #include <asm/mmu_context.h>
 #include <asm/cpcmd.h>
index a8bf9994b0863194d049493b061104b0390edc88..1f77227669e84145385706f39faaf205524e144d 100644 (file)
@@ -32,6 +32,8 @@
 #include <linux/slab.h>
 #include <linux/crash_dump.h>
 #include <asm/asm-offsets.h>
+#include <asm/switch_to.h>
+#include <asm/facility.h>
 #include <asm/ipl.h>
 #include <asm/setup.h>
 #include <asm/irq.h>
index b190eb17a75beb088f90a2b5eccdcbaf13c506b7..ff9e033ce626774fe5b0ad7ddf1c199fe7789471 100644 (file)
@@ -5,6 +5,7 @@ config SUPERH
        select HAVE_IDE if HAS_IOPORT
        select HAVE_MEMBLOCK
        select HAVE_MEMBLOCK_NODE_MAP
+       select ARCH_DISCARD_MEMBLOCK
        select HAVE_OPROFILE
        select HAVE_GENERIC_DMA_COHERENT
        select HAVE_ARCH_TRACEHOOK
@@ -161,6 +162,9 @@ config NO_IOPORT
 config IO_TRAPPED
        bool
 
+config SWAP_IO_SPACE
+       bool
+
 config DMA_COHERENT
        bool
 
index e5ac12b2ce656f6b7037dd53ed07223b20d61dbb..d12fe9ddf3da1a914562b104669273e9b99c28c3 100644 (file)
@@ -522,11 +522,18 @@ static void sdhi0_set_pwr(struct platform_device *pdev, int state)
        gpio_set_value(GPIO_PTB6, state);
 }
 
+static int sdhi0_get_cd(struct platform_device *pdev)
+{
+       return !gpio_get_value(GPIO_PTY7);
+}
+
 static struct sh_mobile_sdhi_info sdhi0_info = {
        .dma_slave_tx   = SHDMA_SLAVE_SDHI0_TX,
        .dma_slave_rx   = SHDMA_SLAVE_SDHI0_RX,
        .set_pwr        = sdhi0_set_pwr,
-       .tmio_caps      = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD,
+       .tmio_caps      = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD |
+                         MMC_CAP_NEEDS_POLL,
+       .get_cd         = sdhi0_get_cd,
 };
 
 static struct resource sdhi0_resources[] = {
@@ -559,11 +566,18 @@ static void sdhi1_set_pwr(struct platform_device *pdev, int state)
        gpio_set_value(GPIO_PTB7, state);
 }
 
+static int sdhi1_get_cd(struct platform_device *pdev)
+{
+       return !gpio_get_value(GPIO_PTW7);
+}
+
 static struct sh_mobile_sdhi_info sdhi1_info = {
        .dma_slave_tx   = SHDMA_SLAVE_SDHI1_TX,
        .dma_slave_rx   = SHDMA_SLAVE_SDHI1_RX,
-       .tmio_caps      = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD,
+       .tmio_caps      = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD |
+                         MMC_CAP_NEEDS_POLL,
        .set_pwr        = sdhi1_set_pwr,
+       .get_cd         = sdhi1_get_cd,
 };
 
 static struct resource sdhi1_resources[] = {
@@ -1001,6 +1015,7 @@ extern char ecovec24_sdram_leave_end;
 static int __init arch_setup(void)
 {
        struct clk *clk;
+       bool cn12_enabled = false;
 
        /* register board specific self-refresh code */
        sh_mobile_register_self_refresh(SUSP_SH_STANDBY | SUSP_SH_SF |
@@ -1201,9 +1216,13 @@ static int __init arch_setup(void)
        gpio_direction_input(GPIO_PTR5);
        gpio_direction_input(GPIO_PTR6);
 
+       /* SD-card slot CN11 */
+       /* Card-detect, used on CN11, either with SDHI0 or with SPI */
+       gpio_request(GPIO_PTY7, NULL);
+       gpio_direction_input(GPIO_PTY7);
+
 #if defined(CONFIG_MMC_SDHI) || defined(CONFIG_MMC_SDHI_MODULE)
        /* enable SDHI0 on CN11 (needs DS2.4 set to ON) */
-       gpio_request(GPIO_FN_SDHI0CD,  NULL);
        gpio_request(GPIO_FN_SDHI0WP,  NULL);
        gpio_request(GPIO_FN_SDHI0CMD, NULL);
        gpio_request(GPIO_FN_SDHI0CLK, NULL);
@@ -1213,23 +1232,6 @@ static int __init arch_setup(void)
        gpio_request(GPIO_FN_SDHI0D0,  NULL);
        gpio_request(GPIO_PTB6, NULL);
        gpio_direction_output(GPIO_PTB6, 0);
-
-#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
-       /* enable SDHI1 on CN12 (needs DS2.6,7 set to ON,OFF) */
-       gpio_request(GPIO_FN_SDHI1CD,  NULL);
-       gpio_request(GPIO_FN_SDHI1WP,  NULL);
-       gpio_request(GPIO_FN_SDHI1CMD, NULL);
-       gpio_request(GPIO_FN_SDHI1CLK, NULL);
-       gpio_request(GPIO_FN_SDHI1D3,  NULL);
-       gpio_request(GPIO_FN_SDHI1D2,  NULL);
-       gpio_request(GPIO_FN_SDHI1D1,  NULL);
-       gpio_request(GPIO_FN_SDHI1D0,  NULL);
-       gpio_request(GPIO_PTB7, NULL);
-       gpio_direction_output(GPIO_PTB7, 0);
-
-       /* I/O buffer drive ability is high for SDHI1 */
-       __raw_writew((__raw_readw(IODRIVEA) & ~0x3000) | 0x2000 , IODRIVEA);
-#endif /* CONFIG_MMC_SH_MMCIF */
 #else
        /* enable MSIOF0 on CN11 (needs DS2.4 set to OFF) */
        gpio_request(GPIO_FN_MSIOF0_TXD, NULL);
@@ -1241,12 +1243,51 @@ static int __init arch_setup(void)
        gpio_direction_output(GPIO_PTB6, 0); /* disable power by default */
        gpio_request(GPIO_PTY6, NULL); /* write protect */
        gpio_direction_input(GPIO_PTY6);
-       gpio_request(GPIO_PTY7, NULL); /* card detect */
-       gpio_direction_input(GPIO_PTY7);
 
        spi_register_board_info(spi_bus, ARRAY_SIZE(spi_bus));
 #endif
 
+       /* MMC/SD-card slot CN12 */
+#if defined(CONFIG_MMC_SH_MMCIF) || defined(CONFIG_MMC_SH_MMCIF_MODULE)
+       /* enable MMCIF (needs DS2.6,7 set to OFF,ON) */
+       gpio_request(GPIO_FN_MMC_D7, NULL);
+       gpio_request(GPIO_FN_MMC_D6, NULL);
+       gpio_request(GPIO_FN_MMC_D5, NULL);
+       gpio_request(GPIO_FN_MMC_D4, NULL);
+       gpio_request(GPIO_FN_MMC_D3, NULL);
+       gpio_request(GPIO_FN_MMC_D2, NULL);
+       gpio_request(GPIO_FN_MMC_D1, NULL);
+       gpio_request(GPIO_FN_MMC_D0, NULL);
+       gpio_request(GPIO_FN_MMC_CLK, NULL);
+       gpio_request(GPIO_FN_MMC_CMD, NULL);
+       gpio_request(GPIO_PTB7, NULL);
+       gpio_direction_output(GPIO_PTB7, 0);
+
+       cn12_enabled = true;
+#elif defined(CONFIG_MMC_SDHI) || defined(CONFIG_MMC_SDHI_MODULE)
+       /* enable SDHI1 on CN12 (needs DS2.6,7 set to ON,OFF) */
+       gpio_request(GPIO_FN_SDHI1WP,  NULL);
+       gpio_request(GPIO_FN_SDHI1CMD, NULL);
+       gpio_request(GPIO_FN_SDHI1CLK, NULL);
+       gpio_request(GPIO_FN_SDHI1D3,  NULL);
+       gpio_request(GPIO_FN_SDHI1D2,  NULL);
+       gpio_request(GPIO_FN_SDHI1D1,  NULL);
+       gpio_request(GPIO_FN_SDHI1D0,  NULL);
+       gpio_request(GPIO_PTB7, NULL);
+       gpio_direction_output(GPIO_PTB7, 0);
+
+       /* Card-detect, used on CN12 with SDHI1 */
+       gpio_request(GPIO_PTW7, NULL);
+       gpio_direction_input(GPIO_PTW7);
+
+       cn12_enabled = true;
+#endif
+
+       if (cn12_enabled)
+               /* I/O buffer drive ability is high for CN12 */
+               __raw_writew((__raw_readw(IODRIVEA) & ~0x3000) | 0x2000,
+                            IODRIVEA);
+
        /* enable Video */
        gpio_request(GPIO_PTU2, NULL);
        gpio_direction_output(GPIO_PTU2, 1);
@@ -1305,25 +1346,6 @@ static int __init arch_setup(void)
        gpio_request(GPIO_PTU5, NULL);
        gpio_direction_output(GPIO_PTU5, 0);
 
-#if defined(CONFIG_MMC_SH_MMCIF) || defined(CONFIG_MMC_SH_MMCIF_MODULE)
-       /* enable MMCIF (needs DS2.6,7 set to OFF,ON) */
-       gpio_request(GPIO_FN_MMC_D7, NULL);
-       gpio_request(GPIO_FN_MMC_D6, NULL);
-       gpio_request(GPIO_FN_MMC_D5, NULL);
-       gpio_request(GPIO_FN_MMC_D4, NULL);
-       gpio_request(GPIO_FN_MMC_D3, NULL);
-       gpio_request(GPIO_FN_MMC_D2, NULL);
-       gpio_request(GPIO_FN_MMC_D1, NULL);
-       gpio_request(GPIO_FN_MMC_D0, NULL);
-       gpio_request(GPIO_FN_MMC_CLK, NULL);
-       gpio_request(GPIO_FN_MMC_CMD, NULL);
-       gpio_request(GPIO_PTB7, NULL);
-       gpio_direction_output(GPIO_PTB7, 0);
-
-       /* I/O buffer drive ability is high for MMCIF */
-       __raw_writew((__raw_readw(IODRIVEA) & ~0x3000) | 0x2000 , IODRIVEA);
-#endif
-
        /* enable I2C device */
        i2c_register_board_info(0, i2c0_devices,
                                ARRAY_SIZE(i2c0_devices));
index e4ea31a62c55ef8ee7a367148e4df9fe32f568a9..58592dfa5cb602ed58eb34dcdead6f1cc66755c6 100644 (file)
@@ -8,8 +8,6 @@
 # Copyright (C) 1999 Stuart Menefy
 #
 
-MKIMAGE := $(srctree)/scripts/mkuboot.sh
-
 #
 # Assign safe dummy values if these variables are not defined,
 # in order to suppress error message.
@@ -61,10 +59,8 @@ KERNEL_ENTRY := $(shell /bin/bash -c 'printf "0x%08x" \
                        $(KERNEL_MEMORY) + \
                        $(CONFIG_ZERO_PAGE_OFFSET) + $(CONFIG_ENTRY_OFFSET)]')
 
-quiet_cmd_uimage = UIMAGE  $@
-      cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A sh -O linux -T kernel \
-                  -C $(2) -a $(KERNEL_LOAD) -e $(KERNEL_ENTRY) \
-                  -n 'Linux-$(KERNELRELEASE)' -d $< $@
+UIMAGE_LOADADDR = $(KERNEL_LOAD)
+UIMAGE_ENTRYADDR = $(KERNEL_ENTRY)
 
 $(obj)/vmlinux.bin: vmlinux FORCE
        $(call if_changed,objcopy)
index be9ca7ca0ce40a028793fa597903470e17e60fd3..e1ab6eb3c04bbabd114e310562c27d25a2ccc0d4 100644 (file)
@@ -181,14 +181,14 @@ static int __init g2_dma_init(void)
 
        ret = register_dmac(&g2_dma_info);
        if (unlikely(ret != 0))
-               free_irq(HW_EVENT_G2_DMA, 0);
+               free_irq(HW_EVENT_G2_DMA, &g2_dma_info);
 
        return ret;
 }
 
 static void __exit g2_dma_exit(void)
 {
-       free_irq(HW_EVENT_G2_DMA, 0);
+       free_irq(HW_EVENT_G2_DMA, &g2_dma_info);
        unregister_dmac(&g2_dma_info);
 }
 
index 3d66a32ce610dd60770081cee2dd149e9b665b9c..c0dd904483c76ffb4d1d53a4f53ac29974b393cf 100644 (file)
@@ -189,8 +189,8 @@ static int __init dmabrg_init(void)
        if (ret == 0)
                return ret;
 
-       free_irq(DMABRGI1, 0);
-out1:  free_irq(DMABRGI0, 0);
+       free_irq(DMABRGI1, NULL);
+out1:  free_irq(DMABRGI0, NULL);
 out0:  kfree(dmabrg_handlers);
        return ret;
 }
index fb8f149907433498d183dda7a7ae9170305f2b14..5a6dab6e27d96deb24353f1e4630594825dc545f 100644 (file)
 #include <asm/mmu.h>
 #include <asm/sizes.h>
 
+#if defined(CONFIG_CPU_BIG_ENDIAN)
+# define PCICR_ENDIANNESS SH4_PCICR_BSWP
+#else
+# define PCICR_ENDIANNESS 0
+#endif
+
+
 static struct resource sh7785_pci_resources[] = {
        {
                .name   = "PCI IO",
@@ -254,7 +261,7 @@ static int __init sh7780_pci_init(void)
        __raw_writel(PCIECR_ENBL, PCIECR);
 
        /* Reset */
-       __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_PRST,
+       __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_PRST | PCICR_ENDIANNESS,
                     chan->reg_base + SH4_PCICR);
 
        /*
@@ -290,7 +297,8 @@ static int __init sh7780_pci_init(void)
         * Now throw it in to register initialization mode and
         * start the real work.
         */
-       __raw_writel(SH4_PCICR_PREFIX, chan->reg_base + SH4_PCICR);
+       __raw_writel(SH4_PCICR_PREFIX | PCICR_ENDIANNESS,
+                    chan->reg_base + SH4_PCICR);
 
        memphys = __pa(memory_start);
        memsize = roundup_pow_of_two(memory_end - memory_start);
@@ -380,7 +388,8 @@ static int __init sh7780_pci_init(void)
         * Initialization mode complete, release the control register and
         * enable round robin mode to stop device overruns/starvation.
         */
-       __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_CFIN | SH4_PCICR_FTO,
+       __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_CFIN | SH4_PCICR_FTO |
+                    PCICR_ENDIANNESS,
                     chan->reg_base + SH4_PCICR);
 
        ret = register_pci_controller(chan);
index 35fc8b077cb1c25048e7c697ea34b979f62e93f4..ec464a6b95fe2cbd790bd63c17302a533f239a6e 100644 (file)
@@ -23,6 +23,7 @@
 #define __IO_PREFIX     generic
 #include <asm/io_generic.h>
 #include <asm/io_trapped.h>
+#include <mach/mangle-port.h>
 
 #define __raw_writeb(v,a)      (__chk_io_ptr(a), *(volatile u8  __force *)(a) = (v))
 #define __raw_writew(v,a)      (__chk_io_ptr(a), *(volatile u16 __force *)(a) = (v))
 #define __raw_readl(a)         (__chk_io_ptr(a), *(volatile u32 __force *)(a))
 #define __raw_readq(a)         (__chk_io_ptr(a), *(volatile u64 __force *)(a))
 
-#define readb_relaxed(c)       ({ u8  __v = __raw_readb(c); __v; })
-#define readw_relaxed(c)       ({ u16 __v = le16_to_cpu((__force __le16) \
-                                       __raw_readw(c)); __v; })
-#define readl_relaxed(c)       ({ u32 __v = le32_to_cpu((__force __le32) \
-                                       __raw_readl(c)); __v; })
-#define readq_relaxed(c)       ({ u64 __v = le64_to_cpu((__force __le64) \
-                                       __raw_readq(c)); __v; })
-
-#define writeb_relaxed(v,c)    ((void)__raw_writeb(v,c))
-#define writew_relaxed(v,c)    ((void)__raw_writew((__force u16) \
-                                       cpu_to_le16(v),c))
-#define writel_relaxed(v,c)    ((void)__raw_writel((__force u32) \
-                                       cpu_to_le32(v),c))
-#define writeq_relaxed(v,c)    ((void)__raw_writeq((__force u64) \
-                                       cpu_to_le64(v),c))
+#define readb_relaxed(c)       ({ u8  __v = ioswabb(__raw_readb(c)); __v; })
+#define readw_relaxed(c)       ({ u16 __v = ioswabw(__raw_readw(c)); __v; })
+#define readl_relaxed(c)       ({ u32 __v = ioswabl(__raw_readl(c)); __v; })
+#define readq_relaxed(c)       ({ u64 __v = ioswabq(__raw_readq(c)); __v; })
+
+#define writeb_relaxed(v,c)    ((void)__raw_writeb((__force  u8)ioswabb(v),c))
+#define writew_relaxed(v,c)    ((void)__raw_writew((__force u16)ioswabw(v),c))
+#define writel_relaxed(v,c)    ((void)__raw_writel((__force u32)ioswabl(v),c))
+#define writeq_relaxed(v,c)    ((void)__raw_writeq((__force u64)ioswabq(v),c))
 
 #define readb(a)               ({ u8  r_ = readb_relaxed(a); rmb(); r_; })
 #define readw(a)               ({ u16 r_ = readw_relaxed(a); rmb(); r_; })
index 65be656ead7d037a03476ce335b6aec7d90049bd..a42a5610a36aa9d4cef7bc386420a0c3089f7fa1 100644 (file)
@@ -1,9 +1,46 @@
 #ifdef __KERNEL__
 # ifdef CONFIG_SUPERH32
+
 #  include "unistd_32.h"
+#  define __ARCH_WANT_SYS_RT_SIGSUSPEND
+
 # else
 #  include "unistd_64.h"
 # endif
+
+# define __ARCH_WANT_IPC_PARSE_VERSION
+# define __ARCH_WANT_OLD_READDIR
+# define __ARCH_WANT_OLD_STAT
+# define __ARCH_WANT_STAT64
+# define __ARCH_WANT_SYS_ALARM
+# define __ARCH_WANT_SYS_GETHOSTNAME
+# define __ARCH_WANT_SYS_IPC
+# define __ARCH_WANT_SYS_PAUSE
+# define __ARCH_WANT_SYS_SGETMASK
+# define __ARCH_WANT_SYS_SIGNAL
+# define __ARCH_WANT_SYS_TIME
+# define __ARCH_WANT_SYS_UTIME
+# define __ARCH_WANT_SYS_WAITPID
+# define __ARCH_WANT_SYS_SOCKETCALL
+# define __ARCH_WANT_SYS_FADVISE64
+# define __ARCH_WANT_SYS_GETPGRP
+# define __ARCH_WANT_SYS_LLSEEK
+# define __ARCH_WANT_SYS_NICE
+# define __ARCH_WANT_SYS_OLD_GETRLIMIT
+# define __ARCH_WANT_SYS_OLD_UNAME
+# define __ARCH_WANT_SYS_OLDUMOUNT
+# define __ARCH_WANT_SYS_SIGPENDING
+# define __ARCH_WANT_SYS_SIGPROCMASK
+# define __ARCH_WANT_SYS_RT_SIGACTION
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+# define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+
 #else
 # ifdef __SH5__
 #  include "unistd_64.h"
index 152b8627a18440b4c54954a8c652edb59ff9bb1c..72fd1e061006058e2e15855016fbf117f62a7fdd 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __ASM_SH_UNISTD_H
-#define __ASM_SH_UNISTD_H
+#ifndef __ASM_SH_UNISTD_32_H
+#define __ASM_SH_UNISTD_32_H
 
 /*
  * Copyright (C) 1999  Niibe Yutaka
@@ -26,7 +26,7 @@
 #define __NR_mknod              14
 #define __NR_chmod              15
 #define __NR_lchown             16
-#define __NR_break              17
+                                /* 17 was sys_break */
 #define __NR_oldstat            18
 #define __NR_lseek              19
 #define __NR_getpid             20
 #define __NR_oldfstat           28
 #define __NR_pause              29
 #define __NR_utime              30
-#define __NR_stty               31
-#define __NR_gtty               32
+                                /* 31 was sys_stty */
+                                /* 32 was sys_gtty */
 #define __NR_access             33
 #define __NR_nice               34
-#define __NR_ftime              35
+                                /* 35 was sys_ftime */
 #define __NR_sync               36
 #define __NR_kill               37
 #define __NR_rename             38
@@ -53,7 +53,7 @@
 #define __NR_dup                41
 #define __NR_pipe               42
 #define __NR_times              43
-#define __NR_prof               44
+                                /* 44 was sys_prof */
 #define __NR_brk                45
 #define __NR_setgid             46
 #define __NR_getgid             47
 #define __NR_getegid            50
 #define __NR_acct               51
 #define __NR_umount2            52
-#define __NR_lock               53
+                                /* 53 was sys_lock */
 #define __NR_ioctl              54
 #define __NR_fcntl              55
-#define __NR_mpx                56
+                                /* 56 was sys_mpx */
 #define __NR_setpgid            57
-#define __NR_ulimit             58
-#define __NR_oldolduname        59
+                                /* 58 was sys_ulimit */
+                                /* 59 was sys_olduname */
 #define __NR_umask              60
 #define __NR_chroot             61
 #define __NR_ustat              62
@@ -91,7 +91,7 @@
 #define __NR_settimeofday       79
 #define __NR_getgroups          80
 #define __NR_setgroups          81
-#define __NR_select             82
+                                /* 82 was sys_oldselect */
 #define __NR_symlink            83
 #define __NR_oldlstat           84
 #define __NR_readlink           85
 #define __NR_fchown             95
 #define __NR_getpriority        96
 #define __NR_setpriority        97
-#define __NR_profil             98
+                                /* 98 was sys_profil */
 #define __NR_statfs             99
 #define __NR_fstatfs           100
-#define __NR_ioperm            101
+                               /* 101 was sys_ioperm */
 #define __NR_socketcall                102
 #define __NR_syslog            103
 #define __NR_setitimer         104
 #define __NR_lstat             107
 #define __NR_fstat             108
 #define __NR_olduname          109
-#define __NR_iopl              110
+                               /* 110 was sys_iopl */
 #define __NR_vhangup           111
-#define __NR_idle              112
-#define __NR_vm86old           113
+                               /* 112 was sys_idle */
+                               /* 113 was sys_vm86old */
 #define __NR_wait4             114
 #define __NR_swapoff           115
 #define __NR_sysinfo           116
 #define __NR_adjtimex          124
 #define __NR_mprotect          125
 #define __NR_sigprocmask       126
-#define __NR_create_module     127
+                               /* 127 was sys_create_module */
 #define __NR_init_module       128
 #define __NR_delete_module     129
-#define __NR_get_kernel_syms   130
+                               /* 130 was sys_get_kernel_syms */
 #define __NR_quotactl          131
 #define __NR_getpgid           132
 #define __NR_fchdir            133
 #define __NR_bdflush           134
 #define __NR_sysfs             135
 #define __NR_personality       136
-#define __NR_afs_syscall       137 /* Syscall for Andrew File System */
+                               /* 137 was sys_afs_syscall */
 #define __NR_setfsuid          138
 #define __NR_setfsgid          139
 #define __NR__llseek           140
 #define __NR_mremap            163
 #define __NR_setresuid         164
 #define __NR_getresuid         165
-#define __NR_vm86              166
-#define __NR_query_module      167
+                               /* 166 was sys_vm86 */
+                               /* 167 was sys_query_module */
 #define __NR_poll              168
 #define __NR_nfsservctl                169
 #define __NR_setresgid         170
 #define __NR_capset            185
 #define __NR_sigaltstack       186
 #define __NR_sendfile          187
-#define __NR_streams1          188     /* some people actually want it */
-#define __NR_streams2          189     /* some people actually want it */
+                               /* 188 reserved for sys_getpmsg */
+                               /* 189 reserved for sys_putpmsg */
 #define __NR_vfork             190
 #define __NR_ugetrlimit                191     /* SuS compliant getrlimit */
 #define __NR_mmap2             192
 #define __NR_madvise           219
 #define __NR_getdents64                220
 #define __NR_fcntl64           221
-/* 223 is unused */
+                               /* 222 is reserved for tux */
+                               /* 223 is unused */
 #define __NR_gettid            224
 #define __NR_readahead         225
 #define __NR_setxattr          226
 #define __NR_futex             240
 #define __NR_sched_setaffinity 241
 #define __NR_sched_getaffinity 242
-#define __NR_set_thread_area   243
-#define __NR_get_thread_area   244
+                               /* 243 is reserved for set_thread_area */
+                               /* 244 is reserved for get_thread_area */
 #define __NR_io_setup          245
 #define __NR_io_destroy                246
 #define __NR_io_getevents      247
 #define __NR_io_submit         248
 #define __NR_io_cancel         249
 #define __NR_fadvise64         250
-
+                               /* 251 is unused */
 #define __NR_exit_group                252
 #define __NR_lookup_dcookie    253
 #define __NR_epoll_create      254
 #define __NR_tgkill            270
 #define __NR_utimes            271
 #define __NR_fadvise64_64      272
-#define __NR_vserver           273
+                               /* 273 is reserved for vserver */
 #define __NR_mbind              274
 #define __NR_get_mempolicy      275
 #define __NR_set_mempolicy      276
 #define __NR_inotify_init      290
 #define __NR_inotify_add_watch 291
 #define __NR_inotify_rm_watch  292
-/* 293 is unused */
+                               /* 293 is unused */
 #define __NR_migrate_pages     294
 #define __NR_openat            295
 #define __NR_mkdirat           296
 
 #define NR_syscalls 367
 
-#ifdef __KERNEL__
-
-#define __ARCH_WANT_IPC_PARSE_VERSION
-#define __ARCH_WANT_OLD_READDIR
-#define __ARCH_WANT_OLD_STAT
-#define __ARCH_WANT_STAT64
-#define __ARCH_WANT_SYS_ALARM
-#define __ARCH_WANT_SYS_GETHOSTNAME
-#define __ARCH_WANT_SYS_IPC
-#define __ARCH_WANT_SYS_PAUSE
-#define __ARCH_WANT_SYS_SGETMASK
-#define __ARCH_WANT_SYS_SIGNAL
-#define __ARCH_WANT_SYS_TIME
-#define __ARCH_WANT_SYS_UTIME
-#define __ARCH_WANT_SYS_WAITPID
-#define __ARCH_WANT_SYS_SOCKETCALL
-#define __ARCH_WANT_SYS_FADVISE64
-#define __ARCH_WANT_SYS_GETPGRP
-#define __ARCH_WANT_SYS_LLSEEK
-#define __ARCH_WANT_SYS_NICE
-#define __ARCH_WANT_SYS_OLD_GETRLIMIT
-#define __ARCH_WANT_SYS_OLD_UNAME
-#define __ARCH_WANT_SYS_OLDUMOUNT
-#define __ARCH_WANT_SYS_SIGPENDING
-#define __ARCH_WANT_SYS_SIGPROCMASK
-#define __ARCH_WANT_SYS_RT_SIGACTION
-#define __ARCH_WANT_SYS_RT_SIGSUSPEND
-
-/*
- * "Conditional" syscalls
- *
- * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
- * but it doesn't work on all toolchains, so we just do it by hand
- */
-#ifndef cond_syscall
-#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
-#endif
-
-#endif /* __KERNEL__ */
-#endif /* __ASM_SH_UNISTD_H */
+#endif /* __ASM_SH_UNISTD_32_H */
index c330c23db5a0675283b6ed0df52ef4e381ef0343..a28edc3296920e03b322257c34c29b565d7871bf 100644 (file)
@@ -31,7 +31,7 @@
 #define __NR_mknod              14
 #define __NR_chmod              15
 #define __NR_lchown             16
-#define __NR_break              17
+                                /* 17 was sys_break */
 #define __NR_oldstat            18
 #define __NR_lseek              19
 #define __NR_getpid             20
 #define __NR_oldfstat           28
 #define __NR_pause              29
 #define __NR_utime              30
-#define __NR_stty               31
-#define __NR_gtty               32
+                                /* 31 was sys_stty */
+                                /* 32 was sys_gtty */
 #define __NR_access             33
 #define __NR_nice               34
-#define __NR_ftime              35
+                                /* 35 was sys_ftime */
 #define __NR_sync               36
 #define __NR_kill               37
 #define __NR_rename             38
@@ -58,7 +58,7 @@
 #define __NR_dup                41
 #define __NR_pipe               42
 #define __NR_times              43
-#define __NR_prof               44
+                                /* 44 was sys_prof */
 #define __NR_brk                45
 #define __NR_setgid             46
 #define __NR_getgid             47
 #define __NR_getegid            50
 #define __NR_acct               51
 #define __NR_umount2            52
-#define __NR_lock               53
+                                /* 53 was sys_lock */
 #define __NR_ioctl              54
 #define __NR_fcntl              55
-#define __NR_mpx                56
+                                /* 56 was sys_mpx */
 #define __NR_setpgid            57
-#define __NR_ulimit             58
-#define __NR_oldolduname        59
+                                /* 58 was sys_ulimit */
+                                /* 59 was sys_olduname */
 #define __NR_umask              60
 #define __NR_chroot             61
 #define __NR_ustat              62
@@ -96,7 +96,7 @@
 #define __NR_settimeofday       79
 #define __NR_getgroups          80
 #define __NR_setgroups          81
-#define __NR_select             82
+                                /* 82 was sys_select */
 #define __NR_symlink            83
 #define __NR_oldlstat           84
 #define __NR_readlink           85
 #define __NR_fchown             95
 #define __NR_getpriority        96
 #define __NR_setpriority        97
-#define __NR_profil             98
+                                /* 98 was sys_profil */
 #define __NR_statfs             99
 #define __NR_fstatfs           100
-#define __NR_ioperm            101
+                               /* 101 was sys_ioperm */
 #define __NR_socketcall                102     /* old implementation of socket systemcall */
 #define __NR_syslog            103
 #define __NR_setitimer         104
 #define __NR_lstat             107
 #define __NR_fstat             108
 #define __NR_olduname          109
-#define __NR_iopl              110
+                               /* 110 was sys_iopl */
 #define __NR_vhangup           111
-#define __NR_idle              112
-#define __NR_vm86old           113
+                               /* 112 was sys_idle */
+                               /* 113 was sys_vm86old */
 #define __NR_wait4             114
 #define __NR_swapoff           115
 #define __NR_sysinfo           116
 #define __NR_adjtimex          124
 #define __NR_mprotect          125
 #define __NR_sigprocmask       126
-#define __NR_create_module     127
+                               /* 127 was sys_create_module */
 #define __NR_init_module       128
 #define __NR_delete_module     129
-#define __NR_get_kernel_syms   130
+                               /* 130 was sys_get_kernel_syms */
 #define __NR_quotactl          131
 #define __NR_getpgid           132
 #define __NR_fchdir            133
 #define __NR_bdflush           134
 #define __NR_sysfs             135
 #define __NR_personality       136
-#define __NR_afs_syscall       137 /* Syscall for Andrew File System */
+                               /* 137 was sys_afs_syscall */
 #define __NR_setfsuid          138
 #define __NR_setfsgid          139
 #define __NR__llseek           140
 #define __NR_mremap            163
 #define __NR_setresuid         164
 #define __NR_getresuid         165
-#define __NR_vm86              166
-#define __NR_query_module      167
+                               /* 166 was sys_vm86 */
+                               /* 167 was sys_query_module */
 #define __NR_poll              168
 #define __NR_nfsservctl                169
 #define __NR_setresgid         170
 #define __NR_capset            185
 #define __NR_sigaltstack       186
 #define __NR_sendfile          187
-#define __NR_streams1          188     /* some people actually want it */
-#define __NR_streams2          189     /* some people actually want it */
+                               /* 188 reserved for getpmsg */
+                               /* 189 reserved for putpmsg */
 #define __NR_vfork             190
 #define __NR_ugetrlimit                191     /* SuS compliant getrlimit */
 #define __NR_mmap2             192
 #define __NR_msgrcv            241
 #define __NR_msgget            242
 #define __NR_msgctl            243
-#if 0
-#define __NR_shmatcall         244
-#endif
+#define __NR_shmat             244
 #define __NR_shmdt             245
 #define __NR_shmget            246
 #define __NR_shmctl            247
 
 #define __NR_getdents64                248
 #define __NR_fcntl64           249
-/* 223 is unused */
+                               /* 250 is reserved for tux */
+                               /* 251 is unused */
 #define __NR_gettid            252
 #define __NR_readahead         253
 #define __NR_setxattr          254
 #define __NR_futex             268
 #define __NR_sched_setaffinity 269
 #define __NR_sched_getaffinity 270
-#define __NR_set_thread_area   271
-#define __NR_get_thread_area   272
+                               /* 271 is reserved for set_thread_area */
+                               /* 272 is reserved for get_thread_area */
 #define __NR_io_setup          273
 #define __NR_io_destroy                274
 #define __NR_io_getevents      275
 #define __NR_io_submit         276
 #define __NR_io_cancel         277
 #define __NR_fadvise64         278
+                               /* 279 is unused */
 #define __NR_exit_group                280
 
 #define __NR_lookup_dcookie    281
 #define __NR_tgkill            298
 #define __NR_utimes            299
 #define __NR_fadvise64_64      300
-#define __NR_vserver           301
-#define __NR_mbind              302
-#define __NR_get_mempolicy      303
-#define __NR_set_mempolicy      304
+                               /* 301 is reserved for vserver */
+                               /* 302 is reserved for mbind */
+                               /* 303 is reserved for get_mempolicy */
+                               /* 304 is reserved for set_mempolicy */
 #define __NR_mq_open            305
 #define __NR_mq_unlink          (__NR_mq_open+1)
 #define __NR_mq_timedsend       (__NR_mq_open+2)
 #define __NR_mq_timedreceive    (__NR_mq_open+3)
 #define __NR_mq_notify          (__NR_mq_open+4)
 #define __NR_mq_getsetattr      (__NR_mq_open+5)
-#define __NR_kexec_load                311
+                               /* 311 is reserved for kexec */
 #define __NR_waitid            312
 #define __NR_add_key           313
 #define __NR_request_key       314
 #define __NR_inotify_init      318
 #define __NR_inotify_add_watch 319
 #define __NR_inotify_rm_watch  320
-/* 321 is unused */
+                               /* 321 is unused */
 #define __NR_migrate_pages     322
 #define __NR_openat            323
 #define __NR_mkdirat           324
 #define __NR_process_vm_readv  376
 #define __NR_process_vm_writev 377
 
-#ifdef __KERNEL__
-
 #define NR_syscalls 378
 
-#define __ARCH_WANT_IPC_PARSE_VERSION
-#define __ARCH_WANT_OLD_READDIR
-#define __ARCH_WANT_OLD_STAT
-#define __ARCH_WANT_STAT64
-#define __ARCH_WANT_SYS_ALARM
-#define __ARCH_WANT_SYS_GETHOSTNAME
-#define __ARCH_WANT_SYS_IPC
-#define __ARCH_WANT_SYS_PAUSE
-#define __ARCH_WANT_SYS_SGETMASK
-#define __ARCH_WANT_SYS_SIGNAL
-#define __ARCH_WANT_SYS_TIME
-#define __ARCH_WANT_SYS_UTIME
-#define __ARCH_WANT_SYS_WAITPID
-#define __ARCH_WANT_SYS_SOCKETCALL
-#define __ARCH_WANT_SYS_FADVISE64
-#define __ARCH_WANT_SYS_GETPGRP
-#define __ARCH_WANT_SYS_LLSEEK
-#define __ARCH_WANT_SYS_NICE
-#define __ARCH_WANT_SYS_OLD_GETRLIMIT
-#define __ARCH_WANT_SYS_OLD_UNAME
-#define __ARCH_WANT_SYS_OLDUMOUNT
-#define __ARCH_WANT_SYS_SIGPENDING
-#define __ARCH_WANT_SYS_SIGPROCMASK
-#define __ARCH_WANT_SYS_RT_SIGACTION
-
-/*
- * "Conditional" syscalls
- *
- * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
- * but it doesn't work on all toolchains, so we just do it by hand
- */
-#ifndef cond_syscall
-#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
-#endif
-
-#endif /* __KERNEL__ */
 #endif /* __ASM_SH_UNISTD_64_H */
index 18fa80aba15e7dee79d9a0125c517a9db01d65d2..02788b6a03b7c9654711b9010ec6cedc1a5edc65 100644 (file)
 
 #define DMAOR_INIT     DMAOR_DME
 
-#if defined(CONFIG_CPU_SUBTYPE_SH7343) || \
-       defined(CONFIG_CPU_SUBTYPE_SH7730)
+#if defined(CONFIG_CPU_SUBTYPE_SH7343)
 #define CHCR_TS_LOW_MASK       0x00000018
 #define CHCR_TS_LOW_SHIFT      3
 #define CHCR_TS_HIGH_MASK      0
 #define CHCR_TS_HIGH_SHIFT     0
 #elif defined(CONFIG_CPU_SUBTYPE_SH7722) || \
+       defined(CONFIG_CPU_SUBTYPE_SH7723) || \
        defined(CONFIG_CPU_SUBTYPE_SH7724) || \
+       defined(CONFIG_CPU_SUBTYPE_SH7730) || \
        defined(CONFIG_CPU_SUBTYPE_SH7786)
 #define CHCR_TS_LOW_MASK       0x00000018
 #define CHCR_TS_LOW_SHIFT      3
 #define CHCR_TS_HIGH_MASK      0x00300000
 #define CHCR_TS_HIGH_SHIFT     (20 - 2)        /* 2 bits for shifted low TS */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
-       defined(CONFIG_CPU_SUBTYPE_SH7764)
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
-#elif defined(CONFIG_CPU_SUBTYPE_SH7757)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \
+       defined(CONFIG_CPU_SUBTYPE_SH7763) || \
+       defined(CONFIG_CPU_SUBTYPE_SH7764) || \
+       defined(CONFIG_CPU_SUBTYPE_SH7780) || \
+       defined(CONFIG_CPU_SUBTYPE_SH7785)
 #define CHCR_TS_LOW_MASK       0x00000018
 #define CHCR_TS_LOW_SHIFT      3
 #define CHCR_TS_HIGH_MASK      0x00100000
 #define CHCR_TS_HIGH_SHIFT     (20 - 2)        /* 2 bits for shifted low TS */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
-#else /* SH7785 */
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
 #endif
 
 /* Transmit sizes and respective CHCR register values */
diff --git a/arch/sh/include/mach-common/mach/mangle-port.h b/arch/sh/include/mach-common/mach/mangle-port.h
new file mode 100644 (file)
index 0000000..4ca1769
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SH version cribbed from the MIPS copy:
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003, 2004 Ralf Baechle
+ */
+#ifndef __MACH_COMMON_MANGLE_PORT_H
+#define __MACH_COMMON_MANGLE_PORT_H
+
+/*
+ * Sane hardware offers swapping of PCI/ISA I/O space accesses in hardware;
+ * less sane hardware forces software to fiddle with this...
+ *
+ * Regardless, if the host bus endianness mismatches that of PCI/ISA, then
+ * you can't have the numerical value of data and byte addresses within
+ * multibyte quantities both preserved at the same time.  Hence two
+ * variations of functions: non-prefixed ones that preserve the value
+ * and prefixed ones that preserve byte addresses.  The latters are
+ * typically used for moving raw data between a peripheral and memory (cf.
+ * string I/O functions), hence the "__mem_" prefix.
+ */
+#if defined(CONFIG_SWAP_IO_SPACE)
+
+# define ioswabb(x)            (x)
+# define __mem_ioswabb(x)      (x)
+# define ioswabw(x)            le16_to_cpu(x)
+# define __mem_ioswabw(x)      (x)
+# define ioswabl(x)            le32_to_cpu(x)
+# define __mem_ioswabl(x)      (x)
+# define ioswabq(x)            le64_to_cpu(x)
+# define __mem_ioswabq(x)      (x)
+
+#else
+
+# define ioswabb(x)            (x)
+# define __mem_ioswabb(x)      (x)
+# define ioswabw(x)            (x)
+# define __mem_ioswabw(x)      cpu_to_le16(x)
+# define ioswabl(x)            (x)
+# define __mem_ioswabl(x)      cpu_to_le32(x)
+# define ioswabq(x)            (x)
+# define __mem_ioswabq(x)      cpu_to_le32(x)
+
+#endif
+
+#endif /* __MACH_COMMON_MANGLE_PORT_H */
index 2875e8be4f7268242f4390d3a02a5dd3311c54a5..c8836cffa2167471ebd3f55eeee28bab1dc758aa 100644 (file)
@@ -680,6 +680,25 @@ static struct platform_device spi1_device = {
        .resource       = spi1_resources,
 };
 
+static struct resource rspi_resources[] = {
+       {
+               .start  = 0xfe480000,
+               .end    = 0xfe4800ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .start  = 220,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device rspi_device = {
+       .name   = "rspi",
+       .id     = 2,
+       .num_resources  = ARRAY_SIZE(rspi_resources),
+       .resource       = rspi_resources,
+};
+
 static struct resource usb_ehci_resources[] = {
        [0] = {
                .start  = 0xfe4f1000,
@@ -740,6 +759,7 @@ static struct platform_device *sh7757_devices[] __initdata = {
        &dma3_device,
        &spi0_device,
        &spi1_device,
+       &rspi_device,
        &usb_ehci_device,
        &usb_ohci_device,
 };
index 6d62eb40e750a2d8ad00919c0d75de477155875f..1ddc876d3b26a836a12b04197374afda66262afa 100644 (file)
@@ -29,7 +29,6 @@ static int cpuidle_sleep_enter(struct cpuidle_device *dev,
                                int index)
 {
        unsigned long allowed_mode = SUSP_SH_SLEEP;
-       ktime_t before, after;
        int requested_state = index;
        int allowed_state;
        int k;
@@ -47,19 +46,16 @@ static int cpuidle_sleep_enter(struct cpuidle_device *dev,
         */
        k = min_t(int, allowed_state, requested_state);
 
-       before = ktime_get();
        sh_mobile_call_standby(cpuidle_mode[k]);
-       after = ktime_get();
-
-       dev->last_residency = (int)ktime_to_ns(ktime_sub(after, before)) >> 10;
 
        return k;
 }
 
 static struct cpuidle_device cpuidle_dev;
 static struct cpuidle_driver cpuidle_driver = {
-       .name =         "sh_idle",
-       .owner =        THIS_MODULE,
+       .name                   = "sh_idle",
+       .owner                  = THIS_MODULE,
+       .en_core_tk_irqen       = 1,
 };
 
 void sh_mobile_setup_cpuidle(void)
index 0fffacea6ed96aebda4c692ace9b1185468d74b5..e68b45b6f3f902aded3f00a4dd3e2341ca518abf 100644 (file)
@@ -3,7 +3,7 @@
  *
  * cpufreq driver for the SuperH processors.
  *
- * Copyright (C) 2002 - 2007 Paul Mundt
+ * Copyright (C) 2002 - 2012 Paul Mundt
  * Copyright (C) 2002 M. R. Brown
  *
  * Clock framework bits from arch/avr32/mach-at32ap/cpufreq.c
@@ -14,6 +14,8 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
+#define pr_fmt(fmt) "cpufreq: " fmt
+
 #include <linux/types.h>
 #include <linux/cpufreq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/cpumask.h>
+#include <linux/cpu.h>
 #include <linux/smp.h>
 #include <linux/sched.h>       /* set_cpus_allowed() */
 #include <linux/clk.h>
+#include <linux/percpu.h>
+#include <linux/sh_clk.h>
 
-static struct clk *cpuclk;
+static DEFINE_PER_CPU(struct clk, sh_cpuclk);
 
 static unsigned int sh_cpufreq_get(unsigned int cpu)
 {
-       return (clk_get_rate(cpuclk) + 500) / 1000;
+       return (clk_get_rate(&per_cpu(sh_cpuclk, cpu)) + 500) / 1000;
 }
 
 /*
@@ -40,8 +45,10 @@ static int sh_cpufreq_target(struct cpufreq_policy *policy,
                             unsigned int relation)
 {
        unsigned int cpu = policy->cpu;
+       struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
        cpumask_t cpus_allowed;
        struct cpufreq_freqs freqs;
+       struct device *dev;
        long freq;
 
        if (!cpu_online(cpu))
@@ -52,13 +59,15 @@ static int sh_cpufreq_target(struct cpufreq_policy *policy,
 
        BUG_ON(smp_processor_id() != cpu);
 
+       dev = get_cpu_device(cpu);
+
        /* Convert target_freq from kHz to Hz */
        freq = clk_round_rate(cpuclk, target_freq * 1000);
 
        if (freq < (policy->min * 1000) || freq > (policy->max * 1000))
                return -EINVAL;
 
-       pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000);
+       dev_dbg(dev, "requested frequency %u Hz\n", target_freq * 1000);
 
        freqs.cpu       = cpu;
        freqs.old       = sh_cpufreq_get(cpu);
@@ -70,78 +79,112 @@ static int sh_cpufreq_target(struct cpufreq_policy *policy,
        clk_set_rate(cpuclk, freq);
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
-       pr_debug("cpufreq: set frequency %lu Hz\n", freq);
+       dev_dbg(dev, "set frequency %lu Hz\n", freq);
+
+       return 0;
+}
+
+static int sh_cpufreq_verify(struct cpufreq_policy *policy)
+{
+       struct clk *cpuclk = &per_cpu(sh_cpuclk, policy->cpu);
+       struct cpufreq_frequency_table *freq_table;
+
+       freq_table = cpuclk->nr_freqs ? cpuclk->freq_table : NULL;
+       if (freq_table)
+               return cpufreq_frequency_table_verify(policy, freq_table);
+
+       cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+                                    policy->cpuinfo.max_freq);
+
+       policy->min = (clk_round_rate(cpuclk, 1) + 500) / 1000;
+       policy->max = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
+
+       cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+                                    policy->cpuinfo.max_freq);
 
        return 0;
 }
 
 static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
-       if (!cpu_online(policy->cpu))
+       unsigned int cpu = policy->cpu;
+       struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
+       struct cpufreq_frequency_table *freq_table;
+       struct device *dev;
+
+       if (!cpu_online(cpu))
                return -ENODEV;
 
-       cpuclk = clk_get(NULL, "cpu_clk");
+       dev = get_cpu_device(cpu);
+
+       cpuclk = clk_get(dev, "cpu_clk");
        if (IS_ERR(cpuclk)) {
-               printk(KERN_ERR "cpufreq: couldn't get CPU#%d clk\n",
-                      policy->cpu);
+               dev_err(dev, "couldn't get CPU clk\n");
                return PTR_ERR(cpuclk);
        }
 
-       /* cpuinfo and default policy values */
-       policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000;
-       policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
-       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+       policy->cur = policy->min = policy->max = sh_cpufreq_get(cpu);
 
-       policy->cur             = sh_cpufreq_get(policy->cpu);
-       policy->min             = policy->cpuinfo.min_freq;
-       policy->max             = policy->cpuinfo.max_freq;
+       freq_table = cpuclk->nr_freqs ? cpuclk->freq_table : NULL;
+       if (freq_table) {
+               int result;
 
-       /*
-        * Catch the cases where the clock framework hasn't been wired up
-        * properly to support scaling.
-        */
-       if (unlikely(policy->min == policy->max)) {
-               printk(KERN_ERR "cpufreq: clock framework rate rounding "
-                      "not supported on CPU#%d.\n", policy->cpu);
+               result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
+               if (!result)
+                       cpufreq_frequency_table_get_attr(freq_table, cpu);
+       } else {
+               dev_notice(dev, "no frequency table found, falling back "
+                          "to rate rounding.\n");
 
-               clk_put(cpuclk);
-               return -EINVAL;
+               policy->cpuinfo.min_freq =
+                       (clk_round_rate(cpuclk, 1) + 500) / 1000;
+               policy->cpuinfo.max_freq =
+                       (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
        }
 
-       printk(KERN_INFO "cpufreq: CPU#%d Frequencies - Minimum %u.%03u MHz, "
+       policy->min = policy->cpuinfo.min_freq;
+       policy->max = policy->cpuinfo.max_freq;
+
+       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+
+       dev_info(dev, "CPU Frequencies - Minimum %u.%03u MHz, "
               "Maximum %u.%03u MHz.\n",
-              policy->cpu, policy->min / 1000, policy->min % 1000,
+              policy->min / 1000, policy->min % 1000,
               policy->max / 1000, policy->max % 1000);
 
        return 0;
 }
 
-static int sh_cpufreq_verify(struct cpufreq_policy *policy)
+static int sh_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 {
-       cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
-                                    policy->cpuinfo.max_freq);
-       return 0;
-}
+       unsigned int cpu = policy->cpu;
+       struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
 
-static int sh_cpufreq_exit(struct cpufreq_policy *policy)
-{
+       cpufreq_frequency_table_put_attr(cpu);
        clk_put(cpuclk);
+
        return 0;
 }
 
+static struct freq_attr *sh_freq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
 static struct cpufreq_driver sh_cpufreq_driver = {
        .owner          = THIS_MODULE,
        .name           = "sh",
-       .init           = sh_cpufreq_cpu_init,
-       .verify         = sh_cpufreq_verify,
-       .target         = sh_cpufreq_target,
        .get            = sh_cpufreq_get,
-       .exit           = sh_cpufreq_exit,
+       .target         = sh_cpufreq_target,
+       .verify         = sh_cpufreq_verify,
+       .init           = sh_cpufreq_cpu_init,
+       .exit           = sh_cpufreq_cpu_exit,
+       .attr           = sh_freq_attr,
 };
 
 static int __init sh_cpufreq_module_init(void)
 {
-       printk(KERN_INFO "cpufreq: SuperH CPU frequency driver.\n");
+       pr_notice("SuperH CPU frequency driver.\n");
        return cpufreq_register_driver(&sh_cpufreq_driver);
 }
 
index 0bc58866add1d932c69346cd6169db54bb08a6a5..5901fba3176e519e3c8ecdc947c532b55c7b884a 100644 (file)
@@ -57,12 +57,13 @@ sys_sigsuspend(old_sigset_t mask,
               unsigned long r5, unsigned long r6, unsigned long r7,
               struct pt_regs __regs)
 {
-       mask &= _BLOCKABLE;
-       spin_lock_irq(&current->sighand->siglock);
+       sigset_t blocked;
+
        current->saved_sigmask = current->blocked;
-       siginitset(&current->blocked, mask);
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+
+       mask &= _BLOCKABLE;
+       siginitset(&blocked, mask);
+       set_current_blocked(&blocked);
 
        current->state = TASK_INTERRUPTIBLE;
        schedule();
@@ -239,11 +240,7 @@ asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5,
                goto badframe;
 
        sigdelsetmask(&set, ~_BLOCKABLE);
-
-       spin_lock_irq(&current->sighand->siglock);
-       current->blocked = set;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+       set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->sc, &r0))
                goto badframe;
@@ -273,10 +270,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5,
                goto badframe;
 
        sigdelsetmask(&set, ~_BLOCKABLE);
-       spin_lock_irq(&current->sighand->siglock);
-       current->blocked = set;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+       set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0))
                goto badframe;
@@ -547,17 +541,8 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
        else
                ret = setup_frame(sig, ka, oldset, regs);
 
-       if (ka->sa.sa_flags & SA_ONESHOT)
-               ka->sa.sa_handler = SIG_DFL;
-
-       if (ret == 0) {
-               spin_lock_irq(&current->sighand->siglock);
-               sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
-               if (!(ka->sa.sa_flags & SA_NODEFER))
-                       sigaddset(&current->blocked,sig);
-               recalc_sigpending();
-               spin_unlock_irq(&current->sighand->siglock);
-       }
+       if (ret == 0)
+               block_sigmask(ka, sig);
 
        return ret;
 }
index 6b5603fe274bdbca92a94b15331553388dc2488a..3c9a6f7dcdce81662d31d59a2bcce6cc425b3bca 100644 (file)
@@ -159,14 +159,13 @@ sys_sigsuspend(old_sigset_t mask,
               unsigned long r6, unsigned long r7,
               struct pt_regs * regs)
 {
-       sigset_t saveset;
+       sigset_t saveset, blocked;
 
-       mask &= _BLOCKABLE;
-       spin_lock_irq(&current->sighand->siglock);
        saveset = current->blocked;
-       siginitset(&current->blocked, mask);
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+
+       mask &= _BLOCKABLE;
+       siginitset(&blocked, mask);
+       set_current_blocked(&blocked);
 
        REF_REG_RET = -EINTR;
        while (1) {
@@ -198,11 +197,8 @@ sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize,
        if (copy_from_user(&newset, unewset, sizeof(newset)))
                return -EFAULT;
        sigdelsetmask(&newset, ~_BLOCKABLE);
-       spin_lock_irq(&current->sighand->siglock);
        saveset = current->blocked;
-       current->blocked = newset;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+       set_current_blocked(&newset);
 
        REF_REG_RET = -EINTR;
        while (1) {
@@ -408,11 +404,7 @@ asmlinkage int sys_sigreturn(unsigned long r2, unsigned long r3,
                goto badframe;
 
        sigdelsetmask(&set, ~_BLOCKABLE);
-
-       spin_lock_irq(&current->sighand->siglock);
-       current->blocked = set;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+       set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->sc, &ret))
                goto badframe;
@@ -445,10 +437,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long r2, unsigned long r3,
                goto badframe;
 
        sigdelsetmask(&set, ~_BLOCKABLE);
-       spin_lock_irq(&current->sighand->siglock);
-       current->blocked = set;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+       set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ret))
                goto badframe;
@@ -734,17 +723,8 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
        else
                ret = setup_frame(sig, ka, oldset, regs);
 
-       if (ka->sa.sa_flags & SA_ONESHOT)
-               ka->sa.sa_handler = SIG_DFL;
-
-       if (ret == 0) {
-               spin_lock_irq(&current->sighand->siglock);
-               sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
-               if (!(ka->sa.sa_flags & SA_NODEFER))
-                       sigaddset(&current->blocked,sig);
-               recalc_sigpending();
-               spin_unlock_irq(&current->sighand->siglock);
-       }
+       if (ret == 0)
+               block_sigmask(ka, sig);
 
        return ret;
 }
index ee56a9b1a981679a6968d89f232729f4f8ed012f..4b68f0f79761e404c5830d617547feae2806a712 100644 (file)
@@ -204,8 +204,8 @@ ENTRY(sys_call_table)
        .long sys_capset           /* 185 */
        .long sys_sigaltstack
        .long sys_sendfile
-       .long sys_ni_syscall    /* streams1 */
-       .long sys_ni_syscall    /* streams2 */
+       .long sys_ni_syscall    /* getpmsg */
+       .long sys_ni_syscall    /* putpmsg */
        .long sys_vfork            /* 190 */
        .long sys_getrlimit
        .long sys_mmap2
@@ -259,8 +259,8 @@ ENTRY(sys_call_table)
        .long sys_futex         /* 240 */
        .long sys_sched_setaffinity
        .long sys_sched_getaffinity
-       .long sys_ni_syscall
-       .long sys_ni_syscall
+       .long sys_ni_syscall    /* reserved for set_thread_area */
+       .long sys_ni_syscall    /* reserved for get_thread_area */
        .long sys_io_setup      /* 245 */
        .long sys_io_destroy
        .long sys_io_getevents
index 9af7de26fb7153ac1dffca0aedc32a822e1cde5e..0956345b36efecd9e6729180701aecf423979a86 100644 (file)
@@ -208,8 +208,8 @@ sys_call_table:
        .long sys_capset                /* 185 */
        .long sys_sigaltstack
        .long sys_sendfile
-       .long sys_ni_syscall    /* streams1 */
-       .long sys_ni_syscall    /* streams2 */
+       .long sys_ni_syscall    /* getpmsg */
+       .long sys_ni_syscall    /* putpmsg */
        .long sys_vfork                 /* 190 */
        .long sys_getrlimit
        .long sys_mmap2
@@ -296,8 +296,8 @@ sys_call_table:
        .long sys_futex
        .long sys_sched_setaffinity
        .long sys_sched_getaffinity     /* 270 */
-       .long sys_ni_syscall
-       .long sys_ni_syscall
+       .long sys_ni_syscall            /* reserved for set_thread_area */
+       .long sys_ni_syscall            /* reserved for get_thread_area */
        .long sys_io_setup
        .long sys_io_destroy
        .long sys_io_getevents          /* 275 */
index 9205416b1e67e8db9d243f1013ad4063014f39ca..d56d199c1aa875a94b618e018eae971cc704e22a 100644 (file)
@@ -5,7 +5,6 @@
 
 ROOT_IMG       := /usr/src/root.img
 ELFTOAOUT      := elftoaout
-MKIMAGE        := $(srctree)/scripts/mkuboot.sh
 
 hostprogs-y    := piggyback btfixupprep
 targets                := tftpboot.img btfix.o btfix.S image zImage vmlinux.aout
@@ -92,11 +91,9 @@ $(obj)/image.bin: $(obj)/image FORCE
 $(obj)/image.gz: $(obj)/image.bin
        $(call if_changed,gzip)
 
-quiet_cmd_uimage = UIMAGE  $@
-      cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A sparc -O linux -T kernel \
-               -C gzip -a $(CONFIG_UBOOT_LOAD_ADDR) \
-              -e $(CONFIG_UBOOT_ENTRY_ADDR) -n 'Linux-$(KERNELRELEASE)' \
-               -d $< $@
+UIMAGE_LOADADDR = $(CONFIG_UBOOT_LOAD_ADDR)
+UIMAGE_ENTRYADDR = $(CONFIG_UBOOT_ENTRY_ADDR)
+UIMAGE_COMPRESSION = gzip
 
 quiet_cmd_uimage.o = UIMAGE.O $@
       cmd_uimage.o = $(LD) -Tdata $(CONFIG_UBOOT_FLASH_ADDR) \
index ef8c7c068f53a3b3dfbaef79bcf9a5fa089b60f6..fd9c3f21cbf059d8130fd90d44cf67c3d0641942 100644 (file)
@@ -165,6 +165,7 @@ struct sparc_stackf {
 #ifdef __KERNEL__
 
 #include <linux/threads.h>
+#include <asm/switch_to.h>
 
 static inline int pt_regs_trap_type(struct pt_regs *regs)
 {
@@ -240,6 +241,7 @@ extern unsigned long profile_pc(struct pt_regs *);
 #ifndef __ASSEMBLY__
 
 #ifdef __KERNEL__
+#include <asm/switch_to.h>
 
 static inline bool pt_regs_is_syscall(struct pt_regs *regs)
 {
index 971fd435a281767d80bd75db250afad41b847745..48565c11e82ae2fcc4ef3627abe67ba38ca811b2 100644 (file)
@@ -6,6 +6,8 @@
 #include <linux/jump_label.h>
 #include <linux/memory.h>
 
+#include <asm/cacheflush.h>
+
 #ifdef HAVE_JUMP_LABEL
 
 void arch_jump_label_transform(struct jump_entry *entry,
index 768290a6c028a65d61bd5e356ba58549f8fdde4f..c8759550799f0beb13b9108d60de83b19c2fab04 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/kdebug.h>
 #include <linux/ftrace.h>
 
+#include <asm/cacheflush.h>
 #include <asm/kdebug.h>
 #include <asm/ptrace.h>
 #include <asm/irq.h>
index 133387980b5635f3dfddc2d33609533beec6f1b7..540b2fec09f0b8164eca2c4684dc26292d0d7b55 100644 (file)
@@ -14,6 +14,7 @@
 #include <asm/sbi.h>
 #include <asm/mmu.h>
 #include <asm/tlbflush.h>
+#include <asm/switch_to.h>
 #include <asm/cacheflush.h>
 
 #include "kernel.h"
index 594768686525a9770b72ea09ef3aef19138ae29d..02db9a0412ce05f5b64c8998621559b003eba7eb 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/cpu.h>
 
 #include <asm/cacheflush.h>
+#include <asm/switch_to.h>
 #include <asm/tlbflush.h>
 
 #include "irq.h"
index 79e5f88845d9c5c9088fb9eba7a9d837b9421bc0..ec7fb70b412bdcaffcedb8653d5ca3d89ca15173 100644 (file)
@@ -11,8 +11,6 @@
 # Copyright (C) 2001~2010 GUAN Xue-tao
 #
 
-MKIMAGE := $(srctree)/scripts/mkuboot.sh
-
 targets := Image zImage uImage
 
 $(obj)/Image: vmlinux FORCE
@@ -26,14 +24,8 @@ $(obj)/zImage: $(obj)/compressed/vmlinux FORCE
        $(call if_changed,objcopy)
        @echo '  Kernel: $@ is ready'
 
-quiet_cmd_uimage = UIMAGE  $@
-      cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A unicore -O linux -T kernel \
-                  -C none -a $(LOADADDR) -e $(STARTADDR) \
-                  -n 'Linux-$(KERNELRELEASE)' -d $< $@
-
-$(obj)/uImage: LOADADDR=0x0
-
-$(obj)/uImage: STARTADDR=$(LOADADDR)
+UIMAGE_ARCH = unicore
+UIMAGE_LOADADDR = 0x0
 
 $(obj)/uImage: $(obj)/zImage FORCE
        $(call if_changed,uimage)
index 0f42c2f44311a9332ec538e32d09cd34cb67c396..a415b1f443659132ba216c88309f67d9e5d04a73 100644 (file)
@@ -642,6 +642,7 @@ static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu)
        kfree(buffer.pointer);
        buffer.length = ACPI_ALLOCATE_BUFFER;
        buffer.pointer = NULL;
+       lapic = NULL;
 
        if (!alloc_cpumask_var(&tmp_map, GFP_KERNEL))
                goto out;
@@ -650,7 +651,7 @@ static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu)
                goto free_tmp_map;
 
        cpumask_copy(tmp_map, cpu_present_mask);
-       acpi_register_lapic(physid, lapic->lapic_flags & ACPI_MADT_ENABLED);
+       acpi_register_lapic(physid, ACPI_MADT_ENABLED);
 
        /*
         * If mp_register_lapic successfully generates a new logical cpu
index 40883ffe2da9261b4f34c2997d2b5e533df25648..bb8e03407e183fa24be61cf6c9432c7a7ffe68c9 100644 (file)
@@ -1313,6 +1313,11 @@ static void __init pmu_check_apic(void)
        pr_info("no hardware sampling interrupt available.\n");
 }
 
+static struct attribute_group x86_pmu_format_group = {
+       .name = "format",
+       .attrs = NULL,
+};
+
 static int __init init_hw_perf_events(void)
 {
        struct x86_pmu_quirk *quirk;
@@ -1387,6 +1392,7 @@ static int __init init_hw_perf_events(void)
        }
 
        x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */
+       x86_pmu_format_group.attrs = x86_pmu.format_attrs;
 
        pr_info("... version:                %d\n",     x86_pmu.version);
        pr_info("... bit width:              %d\n",     x86_pmu.cntval_bits);
@@ -1615,6 +1621,9 @@ static int x86_pmu_event_idx(struct perf_event *event)
 {
        int idx = event->hw.idx;
 
+       if (!x86_pmu.attr_rdpmc)
+               return 0;
+
        if (x86_pmu.num_counters_fixed && idx >= X86_PMC_IDX_FIXED) {
                idx -= X86_PMC_IDX_FIXED;
                idx |= 1 << 30;
@@ -1667,6 +1676,7 @@ static struct attribute_group x86_pmu_attr_group = {
 
 static const struct attribute_group *x86_pmu_attr_groups[] = {
        &x86_pmu_attr_group,
+       &x86_pmu_format_group,
        NULL,
 };
 
@@ -1698,14 +1708,19 @@ static struct pmu pmu = {
        .flush_branch_stack     = x86_pmu_flush_branch_stack,
 };
 
-void perf_update_user_clock(struct perf_event_mmap_page *userpg, u64 now)
+void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now)
 {
+       userpg->cap_usr_time = 0;
+       userpg->cap_usr_rdpmc = x86_pmu.attr_rdpmc;
+       userpg->pmc_width = x86_pmu.cntval_bits;
+
        if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
                return;
 
        if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
                return;
 
+       userpg->cap_usr_time = 1;
        userpg->time_mult = this_cpu_read(cyc2ns);
        userpg->time_shift = CYC2NS_SCALE_FACTOR;
        userpg->time_offset = this_cpu_read(cyc2ns_offset) - now;
index 8484e77c211ea663790890cbc06929ad866adce6..6638aaf5449302c2ea2d5d03073f11bbcaf5b6ba 100644 (file)
@@ -339,6 +339,7 @@ struct x86_pmu {
         * sysfs attrs
         */
        int             attr_rdpmc;
+       struct attribute **format_attrs;
 
        /*
         * CPU Hotplug hooks
index dd002faff7a65156fc5dccec44b052331d0d5c81..95e7fe1c5f0bf57c3bf2537a1856b00f81425458 100644 (file)
@@ -404,6 +404,21 @@ static void amd_pmu_cpu_dead(int cpu)
        }
 }
 
+PMU_FORMAT_ATTR(event, "config:0-7,32-35");
+PMU_FORMAT_ATTR(umask, "config:8-15"   );
+PMU_FORMAT_ATTR(edge,  "config:18"     );
+PMU_FORMAT_ATTR(inv,   "config:23"     );
+PMU_FORMAT_ATTR(cmask, "config:24-31"  );
+
+static struct attribute *amd_format_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_inv.attr,
+       &format_attr_cmask.attr,
+       NULL,
+};
+
 static __initconst const struct x86_pmu amd_pmu = {
        .name                   = "AMD",
        .handle_irq             = x86_pmu_handle_irq,
@@ -426,6 +441,8 @@ static __initconst const struct x86_pmu amd_pmu = {
        .get_event_constraints  = amd_get_event_constraints,
        .put_event_constraints  = amd_put_event_constraints,
 
+       .format_attrs           = amd_format_attr,
+
        .cpu_prepare            = amd_pmu_cpu_prepare,
        .cpu_starting           = amd_pmu_cpu_starting,
        .cpu_dead               = amd_pmu_cpu_dead,
@@ -596,6 +613,7 @@ static __initconst const struct x86_pmu amd_pmu_f15h = {
        .cpu_dead               = amd_pmu_cpu_dead,
 #endif
        .cpu_starting           = amd_pmu_cpu_starting,
+       .format_attrs           = amd_format_attr,
 };
 
 __init int amd_pmu_init(void)
index 6a84e7f28f057665c718d858fa794ea32c65652c..26b3e2fef1047a86a7d546d4b7e8326b3ecbad33 100644 (file)
@@ -1431,6 +1431,24 @@ static void core_pmu_enable_all(int added)
        }
 }
 
+PMU_FORMAT_ATTR(event, "config:0-7"    );
+PMU_FORMAT_ATTR(umask, "config:8-15"   );
+PMU_FORMAT_ATTR(edge,  "config:18"     );
+PMU_FORMAT_ATTR(pc,    "config:19"     );
+PMU_FORMAT_ATTR(any,   "config:21"     ); /* v3 + */
+PMU_FORMAT_ATTR(inv,   "config:23"     );
+PMU_FORMAT_ATTR(cmask, "config:24-31"  );
+
+static struct attribute *intel_arch_formats_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_pc.attr,
+       &format_attr_inv.attr,
+       &format_attr_cmask.attr,
+       NULL,
+};
+
 static __initconst const struct x86_pmu core_pmu = {
        .name                   = "core",
        .handle_irq             = x86_pmu_handle_irq,
@@ -1455,6 +1473,7 @@ static __initconst const struct x86_pmu core_pmu = {
        .put_event_constraints  = intel_put_event_constraints,
        .event_constraints      = intel_core_event_constraints,
        .guest_get_msrs         = core_guest_get_msrs,
+       .format_attrs           = intel_arch_formats_attr,
 };
 
 struct intel_shared_regs *allocate_shared_regs(int cpu)
@@ -1553,6 +1572,21 @@ static void intel_pmu_flush_branch_stack(void)
                intel_pmu_lbr_reset();
 }
 
+PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63");
+
+static struct attribute *intel_arch3_formats_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_pc.attr,
+       &format_attr_any.attr,
+       &format_attr_inv.attr,
+       &format_attr_cmask.attr,
+
+       &format_attr_offcore_rsp.attr, /* XXX do NHM/WSM + SNB breakout */
+       NULL,
+};
+
 static __initconst const struct x86_pmu intel_pmu = {
        .name                   = "Intel",
        .handle_irq             = intel_pmu_handle_irq,
@@ -1576,6 +1610,8 @@ static __initconst const struct x86_pmu intel_pmu = {
        .get_event_constraints  = intel_get_event_constraints,
        .put_event_constraints  = intel_put_event_constraints,
 
+       .format_attrs           = intel_arch3_formats_attr,
+
        .cpu_prepare            = intel_pmu_cpu_prepare,
        .cpu_starting           = intel_pmu_cpu_starting,
        .cpu_dying              = intel_pmu_cpu_dying,
index c7181befecde63e38a5691368a2d77c6de075b05..32bcfc7dd2300d2043245e22a2351dfa65615484 100644 (file)
@@ -87,6 +87,23 @@ static void p6_pmu_enable_event(struct perf_event *event)
        (void)checking_wrmsrl(hwc->config_base, val);
 }
 
+PMU_FORMAT_ATTR(event, "config:0-7"    );
+PMU_FORMAT_ATTR(umask, "config:8-15"   );
+PMU_FORMAT_ATTR(edge,  "config:18"     );
+PMU_FORMAT_ATTR(pc,    "config:19"     );
+PMU_FORMAT_ATTR(inv,   "config:23"     );
+PMU_FORMAT_ATTR(cmask, "config:24-31"  );
+
+static struct attribute *intel_p6_formats_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_pc.attr,
+       &format_attr_inv.attr,
+       &format_attr_cmask.attr,
+       NULL,
+};
+
 static __initconst const struct x86_pmu p6_pmu = {
        .name                   = "p6",
        .handle_irq             = x86_pmu_handle_irq,
@@ -115,6 +132,8 @@ static __initconst const struct x86_pmu p6_pmu = {
        .cntval_mask            = (1ULL << 32) - 1,
        .get_event_constraints  = x86_get_event_constraints,
        .event_constraints      = p6_event_constraints,
+
+       .format_attrs           = intel_p6_formats_attr,
 };
 
 __init int p6_pmu_init(void)
index ce13315d48fb73d9d55f8344e99e6c50bbe168db..6e1e406038c23d963ee12fcab76abca544cc1d86 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/tboot.h>
 #include <linux/stackprotector.h>
 #include <linux/gfp.h>
+#include <linux/cpuidle.h>
 
 #include <asm/acpi.h>
 #include <asm/desc.h>
@@ -1404,7 +1405,8 @@ void native_play_dead(void)
        tboot_shutdown(TB_SHUTDOWN_WFS);
 
        mwait_play_dead();      /* Only returns on failure */
-       hlt_play_dead();
+       if (cpuidle_play_dead())
+               hlt_play_dead();
 }
 
 #else /* ... !CONFIG_HOTPLUG_CPU */
index e2410e27f97ef94d7b87d8338b61f5b7bb87611e..6410744ac5cb7249544fb9424853b0e88f28679f 100644 (file)
@@ -272,7 +272,7 @@ static void tboot_copy_fadt(const struct acpi_table_fadt *fadt)
                offsetof(struct acpi_table_facs, firmware_waking_vector);
 }
 
-void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
+static int tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
 {
        static u32 acpi_shutdown_map[ACPI_S_STATE_COUNT] = {
                /* S0,1,2: */ -1, -1, -1,
@@ -281,7 +281,7 @@ void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
                /* S5: */ TB_SHUTDOWN_S5 };
 
        if (!tboot_enabled())
-               return;
+               return 0;
 
        tboot_copy_fadt(&acpi_gbl_FADT);
        tboot->acpi_sinfo.pm1a_cnt_val = pm1a_control;
@@ -292,10 +292,11 @@ void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
        if (sleep_state >= ACPI_S_STATE_COUNT ||
            acpi_shutdown_map[sleep_state] == -1) {
                pr_warning("unsupported sleep state 0x%x\n", sleep_state);
-               return;
+               return -1;
        }
 
        tboot_shutdown(acpi_shutdown_map[sleep_state]);
+       return 0;
 }
 
 static atomic_t ap_wfs_count;
@@ -345,6 +346,8 @@ static __init int tboot_late_init(void)
 
        atomic_set(&ap_wfs_count, 0);
        register_hotcpu_notifier(&tboot_cpu_notifier);
+
+       acpi_os_set_prepare_sleep(&tboot_sleep);
        return 0;
 }
 
index 7556913aba45963fd9c674f222634eb0cc2341e4..47768ff87343d27d2cc5228aa6e02f6cfdff20a0 100644 (file)
@@ -384,6 +384,15 @@ config ACPI_CUSTOM_METHOD
          load additional kernel modules after boot, this feature may be used
          to override that restriction).
 
+config ACPI_BGRT
+        tristate "Boottime Graphics Resource Table support"
+        default n
+        help
+         This driver adds support for exposing the ACPI Boottime Graphics
+         Resource Table, which allows the operating system to obtain
+         data from the firmware boot splash. It will appear under
+         /sys/firmware/acpi/bgrt/ .
+
 source "drivers/acpi/apei/Kconfig"
 
 endif  # ACPI
index 1567028d2038ecbe853bfebc7b4e4debd03be8fe..47199e2a9130a6c929290e34917fc0fdc0714519 100644 (file)
@@ -62,6 +62,7 @@ obj-$(CONFIG_ACPI_SBS)                += sbs.o
 obj-$(CONFIG_ACPI_HED)         += hed.o
 obj-$(CONFIG_ACPI_EC_DEBUGFS)  += ec_sys.o
 obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
+obj-$(CONFIG_ACPI_BGRT)                += bgrt.o
 
 # processor has its own "processor." module_param namespace
 processor-y                    := processor_driver.o processor_throttling.o
index 0ca208b6dcf099e4b012ed29efa0d564227f20c6..793b8cc8e256c87a743ae7011d731f25393b36be 100644 (file)
@@ -68,12 +68,14 @@ acpi-y +=           \
 
 acpi-y +=              \
        hwacpi.o        \
+       hwesleep.o      \
        hwgpe.o         \
        hwpci.o         \
        hwregs.o        \
        hwsleep.o       \
        hwvalid.o       \
-       hwxface.o
+       hwxface.o       \
+       hwxfsleep.o
 
 acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o
 
index a44bd424f9f4137b58a722e313d3454b0fbc2243..8a7d51bfb3b39632f6f5a46f5b0bb469dfab7b86 100644 (file)
@@ -51,7 +51,6 @@
  *
  * Note: The order of these include files is important.
  */
-#include "acconfig.h"          /* Global configuration constants */
 #include "acmacros.h"          /* C macros */
 #include "aclocal.h"           /* Internal data types */
 #include "acobject.h"          /* ACPI internal object */
index deaa8197956133850767828e05dd248ea41378cf..5e8abb07724ff0b7b55511ca1bc80d0b835e34d7 100644 (file)
@@ -111,7 +111,7 @@ acpi_status acpi_db_find_name_in_namespace(char *name_arg);
 
 void acpi_db_set_scope(char *name);
 
-acpi_status acpi_db_sleep(char *object_arg);
+ACPI_HW_DEPENDENT_RETURN_OK(acpi_status acpi_db_sleep(char *object_arg))
 
 void acpi_db_find_references(char *object_arg);
 
@@ -119,11 +119,13 @@ void acpi_db_display_locks(void);
 
 void acpi_db_display_resources(char *object_arg);
 
-void acpi_db_display_gpes(void);
+ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_db_display_gpes(void))
 
 void acpi_db_check_integrity(void);
 
-void acpi_db_generate_gpe(char *gpe_arg, char *block_arg);
+ACPI_HW_DEPENDENT_RETURN_VOID(void
+                             acpi_db_generate_gpe(char *gpe_arg,
+                                                  char *block_arg))
 
 void acpi_db_check_predefined_names(void);
 
index c53caa521a30e90b46f3d6de1d70db4c7d4ea12b..d700f63e4701462a159af8c3b1c3a9a5112aeada 100644 (file)
@@ -69,11 +69,10 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node,
  */
 acpi_status acpi_ev_init_global_lock_handler(void);
 
-acpi_status acpi_ev_acquire_global_lock(u16 timeout);
-
-acpi_status acpi_ev_release_global_lock(void);
-
-acpi_status acpi_ev_remove_global_lock_handler(void);
+ACPI_HW_DEPENDENT_RETURN_OK(acpi_status
+                           acpi_ev_acquire_global_lock(u16 timeout))
+ ACPI_HW_DEPENDENT_RETURN_OK(acpi_status acpi_ev_release_global_lock(void))
+ acpi_status acpi_ev_remove_global_lock_handler(void);
 
 /*
  * evgpe - Low-level GPE support
@@ -114,7 +113,9 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
                             struct acpi_gpe_block_info *gpe_block,
                             void *context);
 
-acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block);
+ACPI_HW_DEPENDENT_RETURN_OK(acpi_status
+                           acpi_ev_delete_gpe_block(struct acpi_gpe_block_info
+                                                    *gpe_block))
 
 u32
 acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
@@ -126,9 +127,10 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
  */
 acpi_status acpi_ev_gpe_initialize(void);
 
-void acpi_ev_update_gpes(acpi_owner_id table_owner_id);
+ACPI_HW_DEPENDENT_RETURN_VOID(void
+                             acpi_ev_update_gpes(acpi_owner_id table_owner_id))
 
-acpi_status
+ acpi_status
 acpi_ev_match_gpe_method(acpi_handle obj_handle,
                         u32 level, void *context, void **return_value);
 
@@ -237,6 +239,5 @@ acpi_status acpi_ev_remove_sci_handler(void);
 
 u32 acpi_ev_initialize_sCI(u32 program_sCI);
 
-void acpi_ev_terminate(void);
-
+ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_ev_terminate(void))
 #endif                         /* __ACEVENTS_H__  */
index 2853f7673f3bdf05235384b21a614bd1ac331084..4f7d3f57d05cdd24f047ca2af28209abd631e7ae 100644 (file)
@@ -147,7 +147,7 @@ u8 acpi_gbl_system_awake_and_running;
  */
 u8 acpi_gbl_reduced_hardware;
 
-#endif
+#endif                         /* DEFINE_ACPI_GLOBALS */
 
 /* Do not disassemble buffers to resource descriptors */
 
@@ -184,8 +184,12 @@ ACPI_EXTERN u32 acpi_gbl_trace_dbg_layer;
  * found in the RSDT/XSDT.
  */
 ACPI_EXTERN struct acpi_table_list acpi_gbl_root_table_list;
+
+#if (!ACPI_REDUCED_HARDWARE)
 ACPI_EXTERN struct acpi_table_facs *acpi_gbl_FACS;
 
+#endif                         /* !ACPI_REDUCED_HARDWARE */
+
 /* These addresses are calculated from the FADT Event Block addresses */
 
 ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1a_status;
@@ -397,10 +401,15 @@ ACPI_EXTERN struct acpi_fixed_event_handler
 ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head;
 ACPI_EXTERN struct acpi_gpe_block_info
 *acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS];
+
+#if (!ACPI_REDUCED_HARDWARE)
+
 ACPI_EXTERN u8 acpi_gbl_all_gpes_initialized;
 ACPI_EXTERN ACPI_GBL_EVENT_HANDLER acpi_gbl_global_event_handler;
 ACPI_EXTERN void *acpi_gbl_global_event_handler_context;
 
+#endif                         /* !ACPI_REDUCED_HARDWARE */
+
 /*****************************************************************************
  *
  * Debugger globals
index 677793e938f5d83e31241206724b58fec3e01630..5ccb99ae3a6fae1872a13ab40755aa4ecd693106 100644 (file)
@@ -80,6 +80,26 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value);
 
 acpi_status acpi_hw_clear_acpi_status(void);
 
+/*
+ * hwsleep - sleep/wake support (Legacy sleep registers)
+ */
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags);
+
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags);
+
+acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags);
+
+/*
+ * hwesleep - sleep/wake support (Extended FADT-V5 sleep registers)
+ */
+void acpi_hw_execute_sleep_method(char *method_name, u32 integer_argument);
+
+acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags);
+
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags);
+
+acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags);
+
 /*
  * hwvalid - Port I/O with validation
  */
@@ -128,16 +148,4 @@ acpi_status
 acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
                      acpi_handle root_pci_device, acpi_handle pci_region);
 
-#ifdef ACPI_FUTURE_USAGE
-/*
- * hwtimer - ACPI Timer prototypes
- */
-acpi_status acpi_get_timer_resolution(u32 * resolution);
-
-acpi_status acpi_get_timer(u32 * ticks);
-
-acpi_status
-acpi_get_timer_duration(u32 start_ticks, u32 end_ticks, u32 * time_elapsed);
-#endif                         /* ACPI_FUTURE_USAGE */
-
 #endif                         /* __ACHWARE_H__ */
index 3f24068837d5ad8e80c7603ac864fa86673bf7b3..e3922ca20e7f26e7eafa98ed4c2d23f2edf0435a 100644 (file)
@@ -370,6 +370,7 @@ struct acpi_predefined_data {
 /* Defines for Flags field above */
 
 #define ACPI_OBJECT_REPAIRED    1
+#define ACPI_OBJECT_WRAPPED     2
 
 /*
  * Bitmapped return value types
index ef338a96f5b28761b77840851d73fedd8f25da8f..f119f473f71af88f61afc53122e5144050468718 100644 (file)
 
 #endif                         /* ACPI_DEBUG_OUTPUT */
 
+#if (!ACPI_REDUCED_HARDWARE)
+#define ACPI_HW_OPTIONAL_FUNCTION(addr)     addr
+#else
+#define ACPI_HW_OPTIONAL_FUNCTION(addr)     NULL
+#endif
+
 /*
  * Some code only gets executed when the debugger is built in.
  * Note that this is entirely independent of whether the
index 2c9e0f049523120f4c03a094ab8b16e0563aea08..9b19d4b8642407ee8650a988bbb31d653652c787 100644 (file)
@@ -283,8 +283,9 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
                      union acpi_operand_object **return_object_ptr);
 
 acpi_status
-acpi_ns_repair_package_list(struct acpi_predefined_data *data,
-                           union acpi_operand_object **obj_desc_ptr);
+acpi_ns_wrap_with_package(struct acpi_predefined_data *data,
+                         union acpi_operand_object *original_object,
+                         union acpi_operand_object **obj_desc_ptr);
 
 acpi_status
 acpi_ns_repair_null_element(struct acpi_predefined_data *data,
index d5bec304c823387bfc652cf7140b5e47dfb0516f..6712965ba8ae68f35aad59b36b2ae7570371f644 100644 (file)
@@ -67,6 +67,11 @@ acpi_status acpi_tb_resize_root_table_list(void);
 
 acpi_status acpi_tb_verify_table(struct acpi_table_desc *table_desc);
 
+struct acpi_table_header *acpi_tb_table_override(struct acpi_table_header
+                                                *table_header,
+                                                struct acpi_table_desc
+                                                *table_desc);
+
 acpi_status
 acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index);
 
index 6729ebe2f1e669b78f7a30e86efd6445720113e5..07e4dc44f81cf4ee6339e504b27467c51b1ebe8f 100644 (file)
@@ -47,7 +47,7 @@
 
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evevent")
-
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /* Local prototypes */
 static acpi_status acpi_ev_fixed_event_initialize(void);
 
@@ -291,3 +291,5 @@ static u32 acpi_ev_fixed_event_dispatch(u32 event)
        return ((acpi_gbl_fixed_event_handlers[event].
                 handler) (acpi_gbl_fixed_event_handlers[event].context));
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 5e5683cb1f0d9445e3d0e0330eaebe3335866cf2..cfeab38795d88876e0881d087f3b2371254db444 100644 (file)
@@ -48,7 +48,7 @@
 
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evglock")
-
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /* Local prototypes */
 static u32 acpi_ev_global_lock_handler(void *context);
 
@@ -339,3 +339,5 @@ acpi_status acpi_ev_release_global_lock(void)
        acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex);
        return_ACPI_STATUS(status);
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 9e88cb6fb25ea6b05a0befc926b3986a66ac9fd3..8ba0e5f170916a55d81244ef679eaa7715365972 100644 (file)
@@ -48,7 +48,7 @@
 
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evgpe")
-
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /* Local prototypes */
 static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context);
 
@@ -766,3 +766,5 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
 
        return_UINT32(ACPI_INTERRUPT_HANDLED);
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index be75339cd5dd2c31731187a791d94b1829b97945..23a3ca86b2eb20cbaee3d1bd1514c45135b6572a 100644 (file)
@@ -48,7 +48,7 @@
 
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evgpeblk")
-
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /* Local prototypes */
 static acpi_status
 acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block,
@@ -504,3 +504,5 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
 
        return_ACPI_STATUS(AE_OK);
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index adf7494da9dbdcfb03e1843ea559adbbf945a889..da0add858f813ff2c2d5e9d9f45adb78794fabc0 100644 (file)
@@ -48,7 +48,7 @@
 
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evgpeinit")
-
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /*
  * Note: History of _PRW support in ACPICA
  *
@@ -440,3 +440,5 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
                          name, gpe_number));
        return_ACPI_STATUS(AE_OK);
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 25073932aa10d373d2e838d8517f7209fe0b4425..3c43796b8361dd67447162f2c39ac2ca45e1d856 100644 (file)
@@ -48,6 +48,7 @@
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evgpeutil")
 
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ev_walk_gpe_list
@@ -374,3 +375,5 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
 
        return_ACPI_STATUS(AE_OK);
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 84966f4164638573082ce23373460df4297b6eb2..51ef9f5e002da1048c4e99d2b8939d0c35976633 100644 (file)
@@ -108,27 +108,30 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node,
        ACPI_FUNCTION_NAME(ev_queue_notify_request);
 
        /*
-        * For value 3 (Ejection Request), some device method may need to be run.
-        * For value 2 (Device Wake) if _PRW exists, the _PS0 method may need
-        *   to be run.
+        * For value 0x03 (Ejection Request), may need to run a device method.
+        * For value 0x02 (Device Wake), if _PRW exists, may need to run
+        *   the _PS0 method.
         * For value 0x80 (Status Change) on the power button or sleep button,
-        *   initiate soft-off or sleep operation?
+        *   initiate soft-off or sleep operation.
+        *
+        * For all cases, simply dispatch the notify to the handler.
         */
        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                         "Dispatching Notify on [%4.4s] Node %p Value 0x%2.2X (%s)\n",
-                         acpi_ut_get_node_name(node), node, notify_value,
-                         acpi_ut_get_notify_name(notify_value)));
+                         "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n",
+                         acpi_ut_get_node_name(node),
+                         acpi_ut_get_type_name(node->type), notify_value,
+                         acpi_ut_get_notify_name(notify_value), node));
 
        /* Get the notify object attached to the NS Node */
 
        obj_desc = acpi_ns_get_attached_object(node);
        if (obj_desc) {
 
-               /* We have the notify object, Get the right handler */
+               /* We have the notify object, Get the correct handler */
 
                switch (node->type) {
 
-                       /* Notify allowed only on these types */
+                       /* Notify is allowed only on these types */
 
                case ACPI_TYPE_DEVICE:
                case ACPI_TYPE_THERMAL:
@@ -152,7 +155,7 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node,
        }
 
        /*
-        * If there is any handler to run, schedule the dispatcher.
+        * If there is a handler to run, schedule the dispatcher.
         * Check for:
         * 1) Global system notify handler
         * 2) Global device notify handler
@@ -270,6 +273,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context)
        acpi_ut_delete_generic_state(notify_info);
 }
 
+#if (!ACPI_REDUCED_HARDWARE)
 /******************************************************************************
  *
  * FUNCTION:    acpi_ev_terminate
@@ -338,3 +342,5 @@ void acpi_ev_terminate(void)
        }
        return_VOID;
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 26065c612e7673d2c376741da9dbcef3dceefb2b..6a57aa2d70d1af6982cbffb8197de6d73cae9847 100644 (file)
@@ -48,7 +48,7 @@
 
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evsci")
-
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /* Local prototypes */
 static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context);
 
@@ -181,3 +181,5 @@ acpi_status acpi_ev_remove_sci_handler(void)
 
        return_ACPI_STATUS(status);
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 61944e89565a31aa7d82dcc291da7296e28a6c93..44bef5744ebb159f383f6b19515cf6346f656582 100644 (file)
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evxface")
 
-/*******************************************************************************
- *
- * FUNCTION:    acpi_install_exception_handler
- *
- * PARAMETERS:  Handler         - Pointer to the handler function for the
- *                                event
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Saves the pointer to the handler function
- *
- ******************************************************************************/
-#ifdef ACPI_FUTURE_USAGE
-acpi_status acpi_install_exception_handler(acpi_exception_handler handler)
-{
-       acpi_status status;
-
-       ACPI_FUNCTION_TRACE(acpi_install_exception_handler);
-
-       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-
-       /* Don't allow two handlers. */
-
-       if (acpi_gbl_exception_handler) {
-               status = AE_ALREADY_EXISTS;
-               goto cleanup;
-       }
-
-       /* Install the handler */
-
-       acpi_gbl_exception_handler = handler;
-
-      cleanup:
-       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
-       return_ACPI_STATUS(status);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_install_exception_handler)
-#endif                         /*  ACPI_FUTURE_USAGE  */
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_install_global_event_handler
- *
- * PARAMETERS:  Handler         - Pointer to the global event handler function
- *              Context         - Value passed to the handler on each event
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Saves the pointer to the handler function. The global handler
- *              is invoked upon each incoming GPE and Fixed Event. It is
- *              invoked at interrupt level at the time of the event dispatch.
- *              Can be used to update event counters, etc.
- *
- ******************************************************************************/
-acpi_status
-acpi_install_global_event_handler(ACPI_GBL_EVENT_HANDLER handler, void *context)
-{
-       acpi_status status;
-
-       ACPI_FUNCTION_TRACE(acpi_install_global_event_handler);
-
-       /* Parameter validation */
-
-       if (!handler) {
-               return_ACPI_STATUS(AE_BAD_PARAMETER);
-       }
-
-       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-
-       /* Don't allow two handlers. */
-
-       if (acpi_gbl_global_event_handler) {
-               status = AE_ALREADY_EXISTS;
-               goto cleanup;
-       }
-
-       acpi_gbl_global_event_handler = handler;
-       acpi_gbl_global_event_handler_context = context;
-
-      cleanup:
-       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
-       return_ACPI_STATUS(status);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_install_global_event_handler)
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_install_fixed_event_handler
- *
- * PARAMETERS:  Event           - Event type to enable.
- *              Handler         - Pointer to the handler function for the
- *                                event
- *              Context         - Value passed to the handler on each GPE
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Saves the pointer to the handler function and then enables the
- *              event.
- *
- ******************************************************************************/
-acpi_status
-acpi_install_fixed_event_handler(u32 event,
-                                acpi_event_handler handler, void *context)
-{
-       acpi_status status;
-
-       ACPI_FUNCTION_TRACE(acpi_install_fixed_event_handler);
-
-       /* Parameter validation */
-
-       if (event > ACPI_EVENT_MAX) {
-               return_ACPI_STATUS(AE_BAD_PARAMETER);
-       }
-
-       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-
-       /* Don't allow two handlers. */
-
-       if (NULL != acpi_gbl_fixed_event_handlers[event].handler) {
-               status = AE_ALREADY_EXISTS;
-               goto cleanup;
-       }
-
-       /* Install the handler before enabling the event */
-
-       acpi_gbl_fixed_event_handlers[event].handler = handler;
-       acpi_gbl_fixed_event_handlers[event].context = context;
-
-       status = acpi_clear_event(event);
-       if (ACPI_SUCCESS(status))
-               status = acpi_enable_event(event, 0);
-       if (ACPI_FAILURE(status)) {
-               ACPI_WARNING((AE_INFO, "Could not enable fixed event 0x%X",
-                             event));
-
-               /* Remove the handler */
-
-               acpi_gbl_fixed_event_handlers[event].handler = NULL;
-               acpi_gbl_fixed_event_handlers[event].context = NULL;
-       } else {
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                 "Enabled fixed event %X, Handler=%p\n", event,
-                                 handler));
-       }
-
-      cleanup:
-       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
-       return_ACPI_STATUS(status);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_install_fixed_event_handler)
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_remove_fixed_event_handler
- *
- * PARAMETERS:  Event           - Event type to disable.
- *              Handler         - Address of the handler
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Disables the event and unregisters the event handler.
- *
- ******************************************************************************/
-acpi_status
-acpi_remove_fixed_event_handler(u32 event, acpi_event_handler handler)
-{
-       acpi_status status = AE_OK;
-
-       ACPI_FUNCTION_TRACE(acpi_remove_fixed_event_handler);
-
-       /* Parameter validation */
-
-       if (event > ACPI_EVENT_MAX) {
-               return_ACPI_STATUS(AE_BAD_PARAMETER);
-       }
-
-       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-
-       /* Disable the event before removing the handler */
-
-       status = acpi_disable_event(event, 0);
-
-       /* Always Remove the handler */
-
-       acpi_gbl_fixed_event_handlers[event].handler = NULL;
-       acpi_gbl_fixed_event_handlers[event].context = NULL;
-
-       if (ACPI_FAILURE(status)) {
-               ACPI_WARNING((AE_INFO,
-                             "Could not write to fixed event enable register 0x%X",
-                             event));
-       } else {
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabled fixed event %X\n",
-                                 event));
-       }
-
-       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
-       return_ACPI_STATUS(status);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler)
 
 /*******************************************************************************
  *
@@ -334,6 +118,7 @@ acpi_add_handler_object(struct acpi_object_notify_handler *parent_obj,
        return AE_OK;
 }
 
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_install_notify_handler
@@ -703,6 +488,224 @@ acpi_remove_notify_handler(acpi_handle device,
 
 ACPI_EXPORT_SYMBOL(acpi_remove_notify_handler)
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_install_exception_handler
+ *
+ * PARAMETERS:  Handler         - Pointer to the handler function for the
+ *                                event
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function
+ *
+ ******************************************************************************/
+#ifdef ACPI_FUTURE_USAGE
+acpi_status acpi_install_exception_handler(acpi_exception_handler handler)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_install_exception_handler);
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       /* Don't allow two handlers. */
+
+       if (acpi_gbl_exception_handler) {
+               status = AE_ALREADY_EXISTS;
+               goto cleanup;
+       }
+
+       /* Install the handler */
+
+       acpi_gbl_exception_handler = handler;
+
+      cleanup:
+       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+       return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_exception_handler)
+#endif                         /*  ACPI_FUTURE_USAGE  */
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_install_global_event_handler
+ *
+ * PARAMETERS:  Handler         - Pointer to the global event handler function
+ *              Context         - Value passed to the handler on each event
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function. The global handler
+ *              is invoked upon each incoming GPE and Fixed Event. It is
+ *              invoked at interrupt level at the time of the event dispatch.
+ *              Can be used to update event counters, etc.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_install_global_event_handler(ACPI_GBL_EVENT_HANDLER handler, void *context)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_install_global_event_handler);
+
+       /* Parameter validation */
+
+       if (!handler) {
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       /* Don't allow two handlers. */
+
+       if (acpi_gbl_global_event_handler) {
+               status = AE_ALREADY_EXISTS;
+               goto cleanup;
+       }
+
+       acpi_gbl_global_event_handler = handler;
+       acpi_gbl_global_event_handler_context = context;
+
+      cleanup:
+       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+       return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_global_event_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_install_fixed_event_handler
+ *
+ * PARAMETERS:  Event           - Event type to enable.
+ *              Handler         - Pointer to the handler function for the
+ *                                event
+ *              Context         - Value passed to the handler on each GPE
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function and then enables the
+ *              event.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_install_fixed_event_handler(u32 event,
+                                acpi_event_handler handler, void *context)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_install_fixed_event_handler);
+
+       /* Parameter validation */
+
+       if (event > ACPI_EVENT_MAX) {
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       /* Don't allow two handlers. */
+
+       if (NULL != acpi_gbl_fixed_event_handlers[event].handler) {
+               status = AE_ALREADY_EXISTS;
+               goto cleanup;
+       }
+
+       /* Install the handler before enabling the event */
+
+       acpi_gbl_fixed_event_handlers[event].handler = handler;
+       acpi_gbl_fixed_event_handlers[event].context = context;
+
+       status = acpi_clear_event(event);
+       if (ACPI_SUCCESS(status))
+               status = acpi_enable_event(event, 0);
+       if (ACPI_FAILURE(status)) {
+               ACPI_WARNING((AE_INFO, "Could not enable fixed event 0x%X",
+                             event));
+
+               /* Remove the handler */
+
+               acpi_gbl_fixed_event_handlers[event].handler = NULL;
+               acpi_gbl_fixed_event_handlers[event].context = NULL;
+       } else {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                 "Enabled fixed event %X, Handler=%p\n", event,
+                                 handler));
+       }
+
+      cleanup:
+       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+       return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_fixed_event_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_remove_fixed_event_handler
+ *
+ * PARAMETERS:  Event           - Event type to disable.
+ *              Handler         - Address of the handler
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Disables the event and unregisters the event handler.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_remove_fixed_event_handler(u32 event, acpi_event_handler handler)
+{
+       acpi_status status = AE_OK;
+
+       ACPI_FUNCTION_TRACE(acpi_remove_fixed_event_handler);
+
+       /* Parameter validation */
+
+       if (event > ACPI_EVENT_MAX) {
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       /* Disable the event before removing the handler */
+
+       status = acpi_disable_event(event, 0);
+
+       /* Always Remove the handler */
+
+       acpi_gbl_fixed_event_handlers[event].handler = NULL;
+       acpi_gbl_fixed_event_handlers[event].context = NULL;
+
+       if (ACPI_FAILURE(status)) {
+               ACPI_WARNING((AE_INFO,
+                             "Could not write to fixed event enable register 0x%X",
+                             event));
+       } else {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabled fixed event %X\n",
+                                 event));
+       }
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+       return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler)
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_install_gpe_handler
@@ -984,3 +987,4 @@ acpi_status acpi_release_global_lock(u32 handle)
 }
 
 ACPI_EXPORT_SYMBOL(acpi_release_global_lock)
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 1768bbec10023613fdce37d84f400907a69c8f8b..77cee5a5e891b5ea5eb22245afd34ea73a070651 100644 (file)
@@ -49,6 +49,7 @@
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evxfevnt")
 
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /*******************************************************************************
  *
  * FUNCTION:    acpi_enable
@@ -352,3 +353,4 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status)
 }
 
 ACPI_EXPORT_SYMBOL(acpi_get_event_status)
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 33388fd69df448155c51154bfe1f5eb332580600..86f9b343ebd404c45833eda4b88fb37a4f6ec5f7 100644 (file)
@@ -50,6 +50,7 @@
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evxfgpe")
 
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /******************************************************************************
  *
  * FUNCTION:    acpi_update_all_gpes
@@ -695,3 +696,4 @@ acpi_get_gpe_device(u32 index, acpi_handle *gpe_device)
 }
 
 ACPI_EXPORT_SYMBOL(acpi_get_gpe_device)
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index d21ec5f0b3a9a3138da2cd21058b5ecdb7cc9259..d0b9ed5df97e29ce83323a7cb6e56d126c5614a2 100644 (file)
@@ -48,6 +48,7 @@
 #define _COMPONENT          ACPI_HARDWARE
 ACPI_MODULE_NAME("hwacpi")
 
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /******************************************************************************
  *
  * FUNCTION:    acpi_hw_set_mode
@@ -166,3 +167,5 @@ u32 acpi_hw_get_mode(void)
                return_UINT32(ACPI_SYS_MODE_LEGACY);
        }
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c
new file mode 100644 (file)
index 0000000..29e8592
--- /dev/null
@@ -0,0 +1,247 @@
+/******************************************************************************
+ *
+ * Name: hwesleep.c - ACPI Hardware Sleep/Wake Support functions for the
+ *                    extended FADT-V5 sleep registers.
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2012, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT          ACPI_HARDWARE
+ACPI_MODULE_NAME("hwesleep")
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_hw_execute_sleep_method
+ *
+ * PARAMETERS:  method_pathname     - Pathname of method to execute
+ *              integer_argument    - Argument to pass to the method
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Execute a sleep/wake related method with one integer argument
+ *              and no return value.
+ *
+ ******************************************************************************/
+void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument)
+{
+       struct acpi_object_list arg_list;
+       union acpi_object arg;
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(hw_execute_sleep_method);
+
+       /* One argument, integer_argument; No return value expected */
+
+       arg_list.count = 1;
+       arg_list.pointer = &arg;
+       arg.type = ACPI_TYPE_INTEGER;
+       arg.integer.value = (u64)integer_argument;
+
+       status = acpi_evaluate_object(NULL, method_pathname, &arg_list, NULL);
+       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+               ACPI_EXCEPTION((AE_INFO, status, "While executing method %s",
+                               method_pathname));
+       }
+
+       return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_hw_extended_sleep
+ *
+ * PARAMETERS:  sleep_state         - Which sleep state to enter
+ *              Flags               - ACPI_EXECUTE_GTS to run optional method
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Enter a system sleep state via the extended FADT sleep
+ *              registers (V5 FADT).
+ *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags)
+{
+       acpi_status status;
+       u8 sleep_type_value;
+       u64 sleep_status;
+
+       ACPI_FUNCTION_TRACE(hw_extended_sleep);
+
+       /* Extended sleep registers must be valid */
+
+       if (!acpi_gbl_FADT.sleep_control.address ||
+           !acpi_gbl_FADT.sleep_status.address) {
+               return_ACPI_STATUS(AE_NOT_EXIST);
+       }
+
+       /* Clear wake status (WAK_STS) */
+
+       status = acpi_write(ACPI_X_WAKE_STATUS, &acpi_gbl_FADT.sleep_status);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       acpi_gbl_system_awake_and_running = FALSE;
+
+       /* Optionally execute _GTS (Going To Sleep) */
+
+       if (flags & ACPI_EXECUTE_GTS) {
+               acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state);
+       }
+
+       /* Flush caches, as per ACPI specification */
+
+       ACPI_FLUSH_CPU_CACHE();
+
+       /*
+        * Set the SLP_TYP and SLP_EN bits.
+        *
+        * Note: We only use the first value returned by the \_Sx method
+        * (acpi_gbl_sleep_type_a) - As per ACPI specification.
+        */
+       ACPI_DEBUG_PRINT((ACPI_DB_INIT,
+                         "Entering sleep state [S%u]\n", sleep_state));
+
+       sleep_type_value =
+           ((acpi_gbl_sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) &
+            ACPI_X_SLEEP_TYPE_MASK);
+
+       status = acpi_write((sleep_type_value | ACPI_X_SLEEP_ENABLE),
+                           &acpi_gbl_FADT.sleep_control);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       /* Wait for transition back to Working State */
+
+       do {
+               status = acpi_read(&sleep_status, &acpi_gbl_FADT.sleep_status);
+               if (ACPI_FAILURE(status)) {
+                       return_ACPI_STATUS(status);
+               }
+
+       } while (!(((u8)sleep_status) & ACPI_X_WAKE_STATUS));
+
+       return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_hw_extended_wake_prep
+ *
+ * PARAMETERS:  sleep_state         - Which sleep state we just exited
+ *              Flags               - ACPI_EXECUTE_BFS to run optional method
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Perform first part of OS-independent ACPI cleanup after
+ *              a sleep. Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags)
+{
+       acpi_status status;
+       u8 sleep_type_value;
+
+       ACPI_FUNCTION_TRACE(hw_extended_wake_prep);
+
+       status = acpi_get_sleep_type_data(ACPI_STATE_S0,
+                                         &acpi_gbl_sleep_type_a,
+                                         &acpi_gbl_sleep_type_b);
+       if (ACPI_SUCCESS(status)) {
+               sleep_type_value =
+                   ((acpi_gbl_sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) &
+                    ACPI_X_SLEEP_TYPE_MASK);
+
+               (void)acpi_write((sleep_type_value | ACPI_X_SLEEP_ENABLE),
+                                &acpi_gbl_FADT.sleep_control);
+       }
+
+       /* Optionally execute _BFS (Back From Sleep) */
+
+       if (flags & ACPI_EXECUTE_BFS) {
+               acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state);
+       }
+       return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_hw_extended_wake
+ *
+ * PARAMETERS:  sleep_state         - Which sleep state we just exited
+ *              Flags               - Reserved, set to zero
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ *              Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags)
+{
+       ACPI_FUNCTION_TRACE(hw_extended_wake);
+
+       /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */
+
+       acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID;
+
+       /* Execute the wake methods */
+
+       acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING);
+       acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state);
+
+       /*
+        * Some BIOS code assumes that WAK_STS will be cleared on resume
+        * and use it to determine whether the system is rebooting or
+        * resuming. Clear WAK_STS for compatibility.
+        */
+       (void)acpi_write(ACPI_X_WAKE_STATUS, &acpi_gbl_FADT.sleep_status);
+       acpi_gbl_system_awake_and_running = TRUE;
+
+       acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING);
+       return_ACPI_STATUS(AE_OK);
+}
index 1a6894afef7972052a08abf756c52f5e8ccad828..25bd28c4ae8d0055afa5fe44779b123508579fbd 100644 (file)
@@ -48,7 +48,7 @@
 
 #define _COMPONENT          ACPI_HARDWARE
 ACPI_MODULE_NAME("hwgpe")
-
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /* Local prototypes */
 static acpi_status
 acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
@@ -479,3 +479,5 @@ acpi_status acpi_hw_enable_all_wakeup_gpes(void)
        status = acpi_ev_walk_gpe_list(acpi_hw_enable_wakeup_gpe_block, NULL);
        return_ACPI_STATUS(status);
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 4ea4eeb51bfdf588dcbc14e5b8830060e7a42378..6b6c83b87b5215859e618f3bd85721438ed62bd8 100644 (file)
@@ -51,6 +51,7 @@
 #define _COMPONENT          ACPI_HARDWARE
 ACPI_MODULE_NAME("hwregs")
 
+#if (!ACPI_REDUCED_HARDWARE)
 /* Local Prototypes */
 static acpi_status
 acpi_hw_read_multiple(u32 *value,
@@ -62,6 +63,8 @@ acpi_hw_write_multiple(u32 value,
                       struct acpi_generic_address *register_a,
                       struct acpi_generic_address *register_b);
 
+#endif                         /* !ACPI_REDUCED_HARDWARE */
+
 /******************************************************************************
  *
  * FUNCTION:    acpi_hw_validate_register
@@ -154,6 +157,7 @@ acpi_hw_validate_register(struct acpi_generic_address *reg,
 acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg)
 {
        u64 address;
+       u64 value64;
        acpi_status status;
 
        ACPI_FUNCTION_NAME(hw_read);
@@ -175,7 +179,9 @@ acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg)
         */
        if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
                status = acpi_os_read_memory((acpi_physical_address)
-                                            address, value, reg->bit_width);
+                                            address, &value64, reg->bit_width);
+
+               *value = (u32)value64;
        } else {                /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
 
                status = acpi_hw_read_port((acpi_io_address)
@@ -225,7 +231,8 @@ acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg)
         */
        if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
                status = acpi_os_write_memory((acpi_physical_address)
-                                             address, value, reg->bit_width);
+                                             address, (u64)value,
+                                             reg->bit_width);
        } else {                /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
 
                status = acpi_hw_write_port((acpi_io_address)
@@ -240,6 +247,7 @@ acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg)
        return (status);
 }
 
+#if (!ACPI_REDUCED_HARDWARE)
 /*******************************************************************************
  *
  * FUNCTION:    acpi_hw_clear_acpi_status
@@ -285,7 +293,7 @@ exit:
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_hw_get_register_bit_mask
+ * FUNCTION:    acpi_hw_get_bit_register_info
  *
  * PARAMETERS:  register_id         - Index of ACPI Register to access
  *
@@ -658,3 +666,5 @@ acpi_hw_write_multiple(u32 value,
 
        return (status);
 }
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 3c4a922a9fc2810b607e47ffd3d799277ffcea5e..0ed85cac32314f956de3092f7087e3ab011b5ba7 100644 (file)
@@ -1,7 +1,7 @@
-
 /******************************************************************************
  *
- * Name: hwsleep.c - ACPI Hardware Sleep/Wake Interface
+ * Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the
+ *                   original/legacy sleep/PM registers.
  *
  *****************************************************************************/
 
  */
 
 #include <acpi/acpi.h>
+#include <linux/acpi.h>
 #include "accommon.h"
-#include "actables.h"
-#include <linux/tboot.h>
 #include <linux/module.h>
 
 #define _COMPONENT          ACPI_HARDWARE
 ACPI_MODULE_NAME("hwsleep")
 
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /*******************************************************************************
  *
- * FUNCTION:    acpi_set_firmware_waking_vector
- *
- * PARAMETERS:  physical_address    - 32-bit physical address of ACPI real mode
- *                                    entry point.
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Sets the 32-bit firmware_waking_vector field of the FACS
- *
- ******************************************************************************/
-acpi_status
-acpi_set_firmware_waking_vector(u32 physical_address)
-{
-       ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);
-
-
-       /*
-        * According to the ACPI specification 2.0c and later, the 64-bit
-        * waking vector should be cleared and the 32-bit waking vector should
-        * be used, unless we want the wake-up code to be called by the BIOS in
-        * Protected Mode.  Some systems (for example HP dv5-1004nr) are known
-        * to fail to resume if the 64-bit vector is used.
-        */
-
-       /* Set the 32-bit vector */
-
-       acpi_gbl_FACS->firmware_waking_vector = physical_address;
-
-       /* Clear the 64-bit vector if it exists */
-
-       if ((acpi_gbl_FACS->length > 32) && (acpi_gbl_FACS->version >= 1)) {
-               acpi_gbl_FACS->xfirmware_waking_vector = 0;
-       }
-
-       return_ACPI_STATUS(AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
-
-#if ACPI_MACHINE_WIDTH == 64
-/*******************************************************************************
- *
- * FUNCTION:    acpi_set_firmware_waking_vector64
- *
- * PARAMETERS:  physical_address    - 64-bit physical address of ACPI protected
- *                                    mode entry point.
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Sets the 64-bit X_firmware_waking_vector field of the FACS, if
- *              it exists in the table. This function is intended for use with
- *              64-bit host operating systems.
- *
- ******************************************************************************/
-acpi_status
-acpi_set_firmware_waking_vector64(u64 physical_address)
-{
-       ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64);
-
-
-       /* Determine if the 64-bit vector actually exists */
-
-       if ((acpi_gbl_FACS->length <= 32) || (acpi_gbl_FACS->version < 1)) {
-               return_ACPI_STATUS(AE_NOT_EXIST);
-       }
-
-       /* Clear 32-bit vector, set the 64-bit X_ vector */
-
-       acpi_gbl_FACS->firmware_waking_vector = 0;
-       acpi_gbl_FACS->xfirmware_waking_vector = physical_address;
-
-       return_ACPI_STATUS(AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64)
-#endif
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_enter_sleep_state_prep
- *
- * PARAMETERS:  sleep_state         - Which sleep state to enter
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Prepare to enter a system sleep state (see ACPI 2.0 spec p 231)
- *              This function must execute with interrupts enabled.
- *              We break sleeping into 2 stages so that OSPM can handle
- *              various OS-specific tasks between the two steps.
- *
- ******************************************************************************/
-acpi_status acpi_enter_sleep_state_prep(u8 sleep_state)
-{
-       acpi_status status;
-       struct acpi_object_list arg_list;
-       union acpi_object arg;
-
-       ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_prep);
-
-       /* _PSW methods could be run here to enable wake-on keyboard, LAN, etc. */
-
-       status = acpi_get_sleep_type_data(sleep_state,
-                                         &acpi_gbl_sleep_type_a,
-                                         &acpi_gbl_sleep_type_b);
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-
-       /* Setup parameter object */
-
-       arg_list.count = 1;
-       arg_list.pointer = &arg;
-
-       arg.type = ACPI_TYPE_INTEGER;
-       arg.integer.value = sleep_state;
-
-       /* Run the _PTS method */
-
-       status = acpi_evaluate_object(NULL, METHOD_NAME__PTS, &arg_list, NULL);
-       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
-               return_ACPI_STATUS(status);
-       }
-
-       /* Setup the argument to _SST */
-
-       switch (sleep_state) {
-       case ACPI_STATE_S0:
-               arg.integer.value = ACPI_SST_WORKING;
-               break;
-
-       case ACPI_STATE_S1:
-       case ACPI_STATE_S2:
-       case ACPI_STATE_S3:
-               arg.integer.value = ACPI_SST_SLEEPING;
-               break;
-
-       case ACPI_STATE_S4:
-               arg.integer.value = ACPI_SST_SLEEP_CONTEXT;
-               break;
-
-       default:
-               arg.integer.value = ACPI_SST_INDICATOR_OFF;     /* Default is off */
-               break;
-       }
-
-       /*
-        * Set the system indicators to show the desired sleep state.
-        * _SST is an optional method (return no error if not found)
-        */
-       status = acpi_evaluate_object(NULL, METHOD_NAME__SST, &arg_list, NULL);
-       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
-               ACPI_EXCEPTION((AE_INFO, status,
-                               "While executing method _SST"));
-       }
-
-       return_ACPI_STATUS(AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep)
-
-static unsigned int gts, bfs;
-module_param(gts, uint, 0644);
-module_param(bfs, uint, 0644);
-MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
-MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_enter_sleep_state
+ * FUNCTION:    acpi_hw_legacy_sleep
  *
  * PARAMETERS:  sleep_state         - Which sleep state to enter
+ *              Flags               - ACPI_EXECUTE_GTS to run optional method
  *
  * RETURN:      Status
  *
- * DESCRIPTION: Enter a system sleep state (see ACPI 2.0 spec p 231)
+ * DESCRIPTION: Enter a system sleep state via the legacy FADT PM registers
  *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
  *
  ******************************************************************************/
-acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags)
 {
-       u32 pm1a_control;
-       u32 pm1b_control;
        struct acpi_bit_register_info *sleep_type_reg_info;
        struct acpi_bit_register_info *sleep_enable_reg_info;
+       u32 pm1a_control;
+       u32 pm1b_control;
        u32 in_value;
-       struct acpi_object_list arg_list;
-       union acpi_object arg;
        acpi_status status;
 
-       ACPI_FUNCTION_TRACE(acpi_enter_sleep_state);
-
-       if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) ||
-           (acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) {
-               ACPI_ERROR((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X",
-                           acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b));
-               return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
-       }
+       ACPI_FUNCTION_TRACE(hw_legacy_sleep);
 
        sleep_type_reg_info =
            acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE);
@@ -271,6 +95,18 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
                return_ACPI_STATUS(status);
        }
 
+       if (sleep_state != ACPI_STATE_S5) {
+               /*
+                * Disable BM arbitration. This feature is contained within an
+                * optional register (PM2 Control), so ignore a BAD_ADDRESS
+                * exception.
+                */
+               status = acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1);
+               if (ACPI_FAILURE(status) && (status != AE_BAD_ADDRESS)) {
+                       return_ACPI_STATUS(status);
+               }
+       }
+
        /*
         * 1) Disable/Clear all GPEs
         * 2) Enable all wakeup GPEs
@@ -286,18 +122,10 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
                return_ACPI_STATUS(status);
        }
 
-       if (gts) {
-               /* Execute the _GTS method */
-
-               arg_list.count = 1;
-               arg_list.pointer = &arg;
-               arg.type = ACPI_TYPE_INTEGER;
-               arg.integer.value = sleep_state;
+       /* Optionally execute _GTS (Going To Sleep) */
 
-               status = acpi_evaluate_object(NULL, METHOD_NAME__GTS, &arg_list, NULL);
-               if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
-                       return_ACPI_STATUS(status);
-               }
+       if (flags & ACPI_EXECUTE_GTS) {
+               acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state);
        }
 
        /* Get current value of PM1A control */
@@ -344,8 +172,12 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
 
        ACPI_FLUSH_CPU_CACHE();
 
-       tboot_sleep(sleep_state, pm1a_control, pm1b_control);
-
+       status = acpi_os_prepare_sleep(sleep_state, pm1a_control,
+                                      pm1b_control);
+       if (ACPI_SKIP(status))
+               return_ACPI_STATUS(AE_OK);
+       if (ACPI_FAILURE(status))
+               return_ACPI_STATUS(status);
        /* Write #2: Write both SLP_TYP + SLP_EN */
 
        status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control);
@@ -375,114 +207,44 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
                }
        }
 
-       /* Wait until we enter sleep state */
-
-       do {
-               status = acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS,
-                                                   &in_value);
-               if (ACPI_FAILURE(status)) {
-                       return_ACPI_STATUS(status);
-               }
-
-               /* Spin until we wake */
-
-       } while (!in_value);
-
-       return_ACPI_STATUS(AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state)
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_enter_sleep_state_s4bios
- *
- * PARAMETERS:  None
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Perform a S4 bios request.
- *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
- *
- ******************************************************************************/
-acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void)
-{
-       u32 in_value;
-       acpi_status status;
-
-       ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_s4bios);
-
-       /* Clear the wake status bit (PM1) */
-
-       status =
-           acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-
-       status = acpi_hw_clear_acpi_status();
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-
-       /*
-        * 1) Disable/Clear all GPEs
-        * 2) Enable all wakeup GPEs
-        */
-       status = acpi_hw_disable_all_gpes();
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-       acpi_gbl_system_awake_and_running = FALSE;
-
-       status = acpi_hw_enable_all_wakeup_gpes();
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-
-       ACPI_FLUSH_CPU_CACHE();
-
-       status = acpi_hw_write_port(acpi_gbl_FADT.smi_command,
-                                   (u32) acpi_gbl_FADT.S4bios_request, 8);
+       /* Wait for transition back to Working State */
 
        do {
-               acpi_os_stall(1000);
                status =
                    acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value);
                if (ACPI_FAILURE(status)) {
                        return_ACPI_STATUS(status);
                }
+
        } while (!in_value);
 
        return_ACPI_STATUS(AE_OK);
 }
 
-ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios)
-
 /*******************************************************************************
  *
- * FUNCTION:    acpi_leave_sleep_state_prep
+ * FUNCTION:    acpi_hw_legacy_wake_prep
  *
- * PARAMETERS:  sleep_state         - Which sleep state we are exiting
+ * PARAMETERS:  sleep_state         - Which sleep state we just exited
+ *              Flags               - ACPI_EXECUTE_BFS to run optional method
  *
  * RETURN:      Status
  *
  * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
  *              sleep.
- *              Called with interrupts DISABLED.
+ *              Called with interrupts ENABLED.
  *
  ******************************************************************************/
-acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
+
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags)
 {
-       struct acpi_object_list arg_list;
-       union acpi_object arg;
        acpi_status status;
        struct acpi_bit_register_info *sleep_type_reg_info;
        struct acpi_bit_register_info *sleep_enable_reg_info;
        u32 pm1a_control;
        u32 pm1b_control;
 
-       ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep);
+       ACPI_FUNCTION_TRACE(hw_legacy_wake_prep);
 
        /*
         * Set SLP_TYPE and SLP_EN to state S0.
@@ -525,27 +287,20 @@ acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
                }
        }
 
-       if (bfs) {
-               /* Execute the _BFS method */
+       /* Optionally execute _BFS (Back From Sleep) */
 
-               arg_list.count = 1;
-               arg_list.pointer = &arg;
-               arg.type = ACPI_TYPE_INTEGER;
-               arg.integer.value = sleep_state;
-
-               status = acpi_evaluate_object(NULL, METHOD_NAME__BFS, &arg_list, NULL);
-               if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
-                       ACPI_EXCEPTION((AE_INFO, status, "During Method _BFS"));
-               }
+       if (flags & ACPI_EXECUTE_BFS) {
+               acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state);
        }
        return_ACPI_STATUS(status);
 }
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_leave_sleep_state
+ * FUNCTION:    acpi_hw_legacy_wake
  *
  * PARAMETERS:  sleep_state         - Which sleep state we just exited
+ *              Flags               - Reserved, set to zero
  *
  * RETURN:      Status
  *
@@ -553,31 +308,17 @@ acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
  *              Called with interrupts ENABLED.
  *
  ******************************************************************************/
-acpi_status acpi_leave_sleep_state(u8 sleep_state)
+
+acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags)
 {
-       struct acpi_object_list arg_list;
-       union acpi_object arg;
        acpi_status status;
 
-       ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
+       ACPI_FUNCTION_TRACE(hw_legacy_wake);
 
        /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */
 
        acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID;
-
-       /* Setup parameter object */
-
-       arg_list.count = 1;
-       arg_list.pointer = &arg;
-       arg.type = ACPI_TYPE_INTEGER;
-
-       /* Ignore any errors from these methods */
-
-       arg.integer.value = ACPI_SST_WAKING;
-       status = acpi_evaluate_object(NULL, METHOD_NAME__SST, &arg_list, NULL);
-       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
-               ACPI_EXCEPTION((AE_INFO, status, "During Method _SST"));
-       }
+       acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING);
 
        /*
         * GPEs must be enabled before _WAK is called as GPEs
@@ -591,46 +332,50 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state)
        if (ACPI_FAILURE(status)) {
                return_ACPI_STATUS(status);
        }
+
        status = acpi_hw_enable_all_runtime_gpes();
        if (ACPI_FAILURE(status)) {
                return_ACPI_STATUS(status);
        }
 
-       arg.integer.value = sleep_state;
-       status = acpi_evaluate_object(NULL, METHOD_NAME__WAK, &arg_list, NULL);
-       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
-               ACPI_EXCEPTION((AE_INFO, status, "During Method _WAK"));
-       }
-       /* TBD: _WAK "sometimes" returns stuff - do we want to look at it? */
+       /*
+        * Now we can execute _WAK, etc. Some machines require that the GPEs
+        * are enabled before the wake methods are executed.
+        */
+       acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state);
 
        /*
-        * Some BIOSes assume that WAK_STS will be cleared on resume and use
-        * it to determine whether the system is rebooting or resuming. Clear
-        * it for compatibility.
+        * Some BIOS code assumes that WAK_STS will be cleared on resume
+        * and use it to determine whether the system is rebooting or
+        * resuming. Clear WAK_STS for compatibility.
         */
        acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, 1);
-
        acpi_gbl_system_awake_and_running = TRUE;
 
        /* Enable power button */
 
        (void)
            acpi_write_bit_register(acpi_gbl_fixed_event_info
-                             [ACPI_EVENT_POWER_BUTTON].
-                             enable_register_id, ACPI_ENABLE_EVENT);
+                                   [ACPI_EVENT_POWER_BUTTON].
+                                   enable_register_id, ACPI_ENABLE_EVENT);
 
        (void)
            acpi_write_bit_register(acpi_gbl_fixed_event_info
-                             [ACPI_EVENT_POWER_BUTTON].
-                             status_register_id, ACPI_CLEAR_STATUS);
+                                   [ACPI_EVENT_POWER_BUTTON].
+                                   status_register_id, ACPI_CLEAR_STATUS);
 
-       arg.integer.value = ACPI_SST_WORKING;
-       status = acpi_evaluate_object(NULL, METHOD_NAME__SST, &arg_list, NULL);
-       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
-               ACPI_EXCEPTION((AE_INFO, status, "During Method _SST"));
+       /*
+        * Enable BM arbitration. This feature is contained within an
+        * optional register (PM2 Control), so ignore a BAD_ADDRESS
+        * exception.
+        */
+       status = acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);
+       if (ACPI_FAILURE(status) && (status != AE_BAD_ADDRESS)) {
+               return_ACPI_STATUS(status);
        }
 
+       acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING);
        return_ACPI_STATUS(status);
 }
 
-ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state)
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index d4973d9da9f1ce3ed876941609fda18a83928678..f1b2c3b94cac0e004669bb4d647497704f224e8e 100644 (file)
@@ -49,6 +49,7 @@
 #define _COMPONENT          ACPI_HARDWARE
 ACPI_MODULE_NAME("hwtimer")
 
+#if (!ACPI_REDUCED_HARDWARE)   /* Entire module */
 /******************************************************************************
  *
  * FUNCTION:    acpi_get_timer_resolution
@@ -187,3 +188,4 @@ acpi_get_timer_duration(u32 start_ticks, u32 end_ticks, u32 * time_elapsed)
 }
 
 ACPI_EXPORT_SYMBOL(acpi_get_timer_duration)
+#endif                         /* !ACPI_REDUCED_HARDWARE */
index 9d38eb6c0d0b9107866f355c4aa9419937ebb883..ab513a972c95b2376703ebee11cad903c7721ba6 100644 (file)
@@ -74,8 +74,7 @@ acpi_status acpi_reset(void)
 
        /* Check if the reset register is supported */
 
-       if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) ||
-           !reset_reg->address) {
+       if (!reset_reg->address) {
                return_ACPI_STATUS(AE_NOT_EXIST);
        }
 
@@ -138,11 +137,6 @@ acpi_status acpi_read(u64 *return_value, struct acpi_generic_address *reg)
                return (status);
        }
 
-       width = reg->bit_width;
-       if (width == 64) {
-               width = 32;     /* Break into two 32-bit transfers */
-       }
-
        /* Initialize entire 64-bit return value to zero */
 
        *return_value = 0;
@@ -154,24 +148,17 @@ acpi_status acpi_read(u64 *return_value, struct acpi_generic_address *reg)
         */
        if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
                status = acpi_os_read_memory((acpi_physical_address)
-                                            address, &value, width);
+                                            address, return_value,
+                                            reg->bit_width);
                if (ACPI_FAILURE(status)) {
                        return (status);
                }
-               *return_value = value;
-
-               if (reg->bit_width == 64) {
-
-                       /* Read the top 32 bits */
+       } else {                /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
 
-                       status = acpi_os_read_memory((acpi_physical_address)
-                                                    (address + 4), &value, 32);
-                       if (ACPI_FAILURE(status)) {
-                               return (status);
-                       }
-                       *return_value |= ((u64)value << 32);
+               width = reg->bit_width;
+               if (width == 64) {
+                       width = 32;     /* Break into two 32-bit transfers */
                }
-       } else {                /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
 
                status = acpi_hw_read_port((acpi_io_address)
                                           address, &value, width);
@@ -231,32 +218,22 @@ acpi_status acpi_write(u64 value, struct acpi_generic_address *reg)
                return (status);
        }
 
-       width = reg->bit_width;
-       if (width == 64) {
-               width = 32;     /* Break into two 32-bit transfers */
-       }
-
        /*
         * Two address spaces supported: Memory or IO. PCI_Config is
         * not supported here because the GAS structure is insufficient
         */
        if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
                status = acpi_os_write_memory((acpi_physical_address)
-                                             address, ACPI_LODWORD(value),
-                                             width);
+                                             address, value, reg->bit_width);
                if (ACPI_FAILURE(status)) {
                        return (status);
                }
+       } else {                /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
 
-               if (reg->bit_width == 64) {
-                       status = acpi_os_write_memory((acpi_physical_address)
-                                                     (address + 4),
-                                                     ACPI_HIDWORD(value), 32);
-                       if (ACPI_FAILURE(status)) {
-                               return (status);
-                       }
+               width = reg->bit_width;
+               if (width == 64) {
+                       width = 32;     /* Break into two 32-bit transfers */
                }
-       } else {                /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
 
                status = acpi_hw_write_port((acpi_io_address)
                                            address, ACPI_LODWORD(value),
@@ -286,6 +263,7 @@ acpi_status acpi_write(u64 value, struct acpi_generic_address *reg)
 
 ACPI_EXPORT_SYMBOL(acpi_write)
 
+#if (!ACPI_REDUCED_HARDWARE)
 /*******************************************************************************
  *
  * FUNCTION:    acpi_read_bit_register
@@ -453,7 +431,7 @@ unlock_and_exit:
 }
 
 ACPI_EXPORT_SYMBOL(acpi_write_bit_register)
-
+#endif                         /* !ACPI_REDUCED_HARDWARE */
 /*******************************************************************************
  *
  * FUNCTION:    acpi_get_sleep_type_data
diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c
new file mode 100644 (file)
index 0000000..762d059
--- /dev/null
@@ -0,0 +1,431 @@
+/******************************************************************************
+ *
+ * Name: hwxfsleep.c - ACPI Hardware Sleep/Wake External Interfaces
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2012, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include <linux/module.h>
+
+#define _COMPONENT          ACPI_HARDWARE
+ACPI_MODULE_NAME("hwxfsleep")
+
+/* Local prototypes */
+static acpi_status
+acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id);
+
+/*
+ * Dispatch table used to efficiently branch to the various sleep
+ * functions.
+ */
+#define ACPI_SLEEP_FUNCTION_ID         0
+#define ACPI_WAKE_PREP_FUNCTION_ID     1
+#define ACPI_WAKE_FUNCTION_ID          2
+
+/* Legacy functions are optional, based upon ACPI_REDUCED_HARDWARE */
+
+static struct acpi_sleep_functions acpi_sleep_dispatch[] = {
+       {ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_sleep),
+        acpi_hw_extended_sleep},
+       {ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_wake_prep),
+        acpi_hw_extended_wake_prep},
+       {ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_wake), acpi_hw_extended_wake}
+};
+
+/*
+ * These functions are removed for the ACPI_REDUCED_HARDWARE case:
+ *      acpi_set_firmware_waking_vector
+ *      acpi_set_firmware_waking_vector64
+ *      acpi_enter_sleep_state_s4bios
+ */
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_set_firmware_waking_vector
+ *
+ * PARAMETERS:  physical_address    - 32-bit physical address of ACPI real mode
+ *                                    entry point.
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Sets the 32-bit firmware_waking_vector field of the FACS
+ *
+ ******************************************************************************/
+
+acpi_status acpi_set_firmware_waking_vector(u32 physical_address)
+{
+       ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);
+
+
+       /*
+        * According to the ACPI specification 2.0c and later, the 64-bit
+        * waking vector should be cleared and the 32-bit waking vector should
+        * be used, unless we want the wake-up code to be called by the BIOS in
+        * Protected Mode.  Some systems (for example HP dv5-1004nr) are known
+        * to fail to resume if the 64-bit vector is used.
+        */
+
+       /* Set the 32-bit vector */
+
+       acpi_gbl_FACS->firmware_waking_vector = physical_address;
+
+       /* Clear the 64-bit vector if it exists */
+
+       if ((acpi_gbl_FACS->length > 32) && (acpi_gbl_FACS->version >= 1)) {
+               acpi_gbl_FACS->xfirmware_waking_vector = 0;
+       }
+
+       return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
+
+#if ACPI_MACHINE_WIDTH == 64
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_set_firmware_waking_vector64
+ *
+ * PARAMETERS:  physical_address    - 64-bit physical address of ACPI protected
+ *                                    mode entry point.
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Sets the 64-bit X_firmware_waking_vector field of the FACS, if
+ *              it exists in the table. This function is intended for use with
+ *              64-bit host operating systems.
+ *
+ ******************************************************************************/
+acpi_status acpi_set_firmware_waking_vector64(u64 physical_address)
+{
+       ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64);
+
+
+       /* Determine if the 64-bit vector actually exists */
+
+       if ((acpi_gbl_FACS->length <= 32) || (acpi_gbl_FACS->version < 1)) {
+               return_ACPI_STATUS(AE_NOT_EXIST);
+       }
+
+       /* Clear 32-bit vector, set the 64-bit X_ vector */
+
+       acpi_gbl_FACS->firmware_waking_vector = 0;
+       acpi_gbl_FACS->xfirmware_waking_vector = physical_address;
+       return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64)
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_enter_sleep_state_s4bios
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Perform a S4 bios request.
+ *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void)
+{
+       u32 in_value;
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_s4bios);
+
+       /* Clear the wake status bit (PM1) */
+
+       status =
+           acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       status = acpi_hw_clear_acpi_status();
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       /*
+        * 1) Disable/Clear all GPEs
+        * 2) Enable all wakeup GPEs
+        */
+       status = acpi_hw_disable_all_gpes();
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+       acpi_gbl_system_awake_and_running = FALSE;
+
+       status = acpi_hw_enable_all_wakeup_gpes();
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       ACPI_FLUSH_CPU_CACHE();
+
+       status = acpi_hw_write_port(acpi_gbl_FADT.smi_command,
+                                   (u32)acpi_gbl_FADT.S4bios_request, 8);
+
+       do {
+               acpi_os_stall(1000);
+               status =
+                   acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value);
+               if (ACPI_FAILURE(status)) {
+                       return_ACPI_STATUS(status);
+               }
+       } while (!in_value);
+
+       return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios)
+#endif                         /* !ACPI_REDUCED_HARDWARE */
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_hw_sleep_dispatch
+ *
+ * PARAMETERS:  sleep_state         - Which sleep state to enter/exit
+ *              function_id         - Sleep, wake_prep, or Wake
+ *
+ * RETURN:      Status from the invoked sleep handling function.
+ *
+ * DESCRIPTION: Dispatch a sleep/wake request to the appropriate handling
+ *              function.
+ *
+ ******************************************************************************/
+static acpi_status
+acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id)
+{
+       acpi_status status;
+       struct acpi_sleep_functions *sleep_functions =
+           &acpi_sleep_dispatch[function_id];
+
+#if (!ACPI_REDUCED_HARDWARE)
+
+       /*
+        * If the Hardware Reduced flag is set (from the FADT), we must
+        * use the extended sleep registers
+        */
+       if (acpi_gbl_reduced_hardware || acpi_gbl_FADT.sleep_control.address) {
+               status = sleep_functions->extended_function(sleep_state, flags);
+       } else {
+               /* Legacy sleep */
+
+               status = sleep_functions->legacy_function(sleep_state, flags);
+       }
+
+       return (status);
+
+#else
+       /*
+        * For the case where reduced-hardware-only code is being generated,
+        * we know that only the extended sleep registers are available
+        */
+       status = sleep_functions->extended_function(sleep_state, flags);
+       return (status);
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_enter_sleep_state_prep
+ *
+ * PARAMETERS:  sleep_state         - Which sleep state to enter
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Prepare to enter a system sleep state.
+ *              This function must execute with interrupts enabled.
+ *              We break sleeping into 2 stages so that OSPM can handle
+ *              various OS-specific tasks between the two steps.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_enter_sleep_state_prep(u8 sleep_state)
+{
+       acpi_status status;
+       struct acpi_object_list arg_list;
+       union acpi_object arg;
+       u32 sst_value;
+
+       ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_prep);
+
+       status = acpi_get_sleep_type_data(sleep_state,
+                                         &acpi_gbl_sleep_type_a,
+                                         &acpi_gbl_sleep_type_b);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       /* Execute the _PTS method (Prepare To Sleep) */
+
+       arg_list.count = 1;
+       arg_list.pointer = &arg;
+       arg.type = ACPI_TYPE_INTEGER;
+       arg.integer.value = sleep_state;
+
+       status =
+           acpi_evaluate_object(NULL, METHOD_PATHNAME__PTS, &arg_list, NULL);
+       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+               return_ACPI_STATUS(status);
+       }
+
+       /* Setup the argument to the _SST method (System STatus) */
+
+       switch (sleep_state) {
+       case ACPI_STATE_S0:
+               sst_value = ACPI_SST_WORKING;
+               break;
+
+       case ACPI_STATE_S1:
+       case ACPI_STATE_S2:
+       case ACPI_STATE_S3:
+               sst_value = ACPI_SST_SLEEPING;
+               break;
+
+       case ACPI_STATE_S4:
+               sst_value = ACPI_SST_SLEEP_CONTEXT;
+               break;
+
+       default:
+               sst_value = ACPI_SST_INDICATOR_OFF;     /* Default is off */
+               break;
+       }
+
+       /*
+        * Set the system indicators to show the desired sleep state.
+        * _SST is an optional method (return no error if not found)
+        */
+       acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, sst_value);
+       return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep)
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_enter_sleep_state
+ *
+ * PARAMETERS:  sleep_state         - Which sleep state to enter
+ *              Flags               - ACPI_EXECUTE_GTS to run optional method
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Enter a system sleep state (see ACPI 2.0 spec p 231)
+ *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_enter_sleep_state);
+
+       if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) ||
+           (acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) {
+               ACPI_ERROR((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X",
+                           acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b));
+               return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+       }
+
+       status =
+           acpi_hw_sleep_dispatch(sleep_state, flags, ACPI_SLEEP_FUNCTION_ID);
+       return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state)
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_leave_sleep_state_prep
+ *
+ * PARAMETERS:  sleep_state         - Which sleep state we are exiting
+ *              Flags               - ACPI_EXECUTE_BFS to run optional method
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
+ *              sleep.
+ *              Called with interrupts DISABLED.
+ *
+ ******************************************************************************/
+acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep);
+
+       status =
+           acpi_hw_sleep_dispatch(sleep_state, flags,
+                                  ACPI_WAKE_PREP_FUNCTION_ID);
+       return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state_prep)
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_leave_sleep_state
+ *
+ * PARAMETERS:  sleep_state         - Which sleep state we are exiting
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ *              Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+acpi_status acpi_leave_sleep_state(u8 sleep_state)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
+
+
+       status = acpi_hw_sleep_dispatch(sleep_state, 0, ACPI_WAKE_FUNCTION_ID);
+       return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state)
index b7f2b3be79ac110182f9c17a3d4cecee4462ebf4..3f7f3f6e7dd5b9ea711f0e31083cb19821b220c6 100644 (file)
@@ -242,7 +242,20 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
 
                if (!obj_desc) {
 
-                       /* No attached object, we are done */
+                       /* No attached object. Some types should always have an object */
+
+                       switch (type) {
+                       case ACPI_TYPE_INTEGER:
+                       case ACPI_TYPE_PACKAGE:
+                       case ACPI_TYPE_BUFFER:
+                       case ACPI_TYPE_STRING:
+                       case ACPI_TYPE_METHOD:
+                               acpi_os_printf("<No attached object>");
+                               break;
+
+                       default:
+                               break;
+                       }
 
                        acpi_os_printf("\n");
                        return (AE_OK);
index 30ea5bc53a78bf75572169b282752f1d70ebf37c..3b5acb0eb40648c4bbcacfca684ccd31a8e4ea51 100644 (file)
@@ -121,7 +121,7 @@ void acpi_ns_dump_root_devices(void)
                return;
        }
 
-       status = acpi_get_handle(NULL, ACPI_NS_SYSTEM_BUS, &sys_bus_handle);
+       status = acpi_get_handle(NULL, METHOD_NAME__SB_, &sys_bus_handle);
        if (ACPI_FAILURE(status)) {
                return;
        }
index bbe46a447d34d2c9597f0894060e6f05af1645de..23ce096864186a1cfd6ced9e57fd17d208646004 100644 (file)
@@ -638,8 +638,8 @@ acpi_ns_check_package(struct acpi_predefined_data *data,
                        /* Create the new outer package and populate it */
 
                        status =
-                           acpi_ns_repair_package_list(data,
-                                                       return_object_ptr);
+                           acpi_ns_wrap_with_package(data, *elements,
+                                                     return_object_ptr);
                        if (ACPI_FAILURE(status)) {
                                return (status);
                        }
index 9c35d20eb52b14fc9c4a5e238017852d934aee7c..5519a64a353f7a7f2d0f36c36a6011f6f5311efc 100644 (file)
@@ -71,11 +71,10 @@ ACPI_MODULE_NAME("nsrepair")
  * Buffer  -> String
  * Buffer  -> Package of Integers
  * Package -> Package of one Package
+ * An incorrect standalone object is wrapped with required outer package
  *
  * Additional possible repairs:
- *
  * Required package elements that are NULL replaced by Integer/String/Buffer
- * Incorrect standalone package wrapped with required outer package
  *
  ******************************************************************************/
 /* Local prototypes */
@@ -91,10 +90,6 @@ static acpi_status
 acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
                          union acpi_operand_object **return_object);
 
-static acpi_status
-acpi_ns_convert_to_package(union acpi_operand_object *original_object,
-                          union acpi_operand_object **return_object);
-
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ns_repair_object
@@ -151,9 +146,24 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
                }
        }
        if (expected_btypes & ACPI_RTYPE_PACKAGE) {
-               status = acpi_ns_convert_to_package(return_object, &new_object);
+               /*
+                * A package is expected. We will wrap the existing object with a
+                * new package object. It is often the case that if a variable-length
+                * package is required, but there is only a single object needed, the
+                * BIOS will return that object instead of wrapping it with a Package
+                * object. Note: after the wrapping, the package will be validated
+                * for correct contents (expected object type or types).
+                */
+               status =
+                   acpi_ns_wrap_with_package(data, return_object, &new_object);
                if (ACPI_SUCCESS(status)) {
-                       goto object_repaired;
+                       /*
+                        * The original object just had its reference count
+                        * incremented for being inserted into the new package.
+                        */
+                       *return_object_ptr = new_object;        /* New Package object */
+                       data->flags |= ACPI_OBJECT_REPAIRED;
+                       return (AE_OK);
                }
        }
 
@@ -165,22 +175,27 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
 
        /* Object was successfully repaired */
 
-       /*
-        * If the original object is a package element, we need to:
-        * 1. Set the reference count of the new object to match the
-        *    reference count of the old object.
-        * 2. Decrement the reference count of the original object.
-        */
        if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
-               new_object->common.reference_count =
-                   return_object->common.reference_count;
+               /*
+                * The original object is a package element. We need to
+                * decrement the reference count of the original object,
+                * for removing it from the package.
+                *
+                * However, if the original object was just wrapped with a
+                * package object as part of the repair, we don't need to
+                * change the reference count.
+                */
+               if (!(data->flags & ACPI_OBJECT_WRAPPED)) {
+                       new_object->common.reference_count =
+                           return_object->common.reference_count;
 
-               if (return_object->common.reference_count > 1) {
-                       return_object->common.reference_count--;
+                       if (return_object->common.reference_count > 1) {
+                               return_object->common.reference_count--;
+                       }
                }
 
                ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
-                                 "%s: Converted %s to expected %s at index %u\n",
+                                 "%s: Converted %s to expected %s at Package index %u\n",
                                  data->pathname,
                                  acpi_ut_get_object_type_name(return_object),
                                  acpi_ut_get_object_type_name(new_object),
@@ -451,65 +466,6 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
        return (AE_OK);
 }
 
-/*******************************************************************************
- *
- * FUNCTION:    acpi_ns_convert_to_package
- *
- * PARAMETERS:  original_object     - Object to be converted
- *              return_object       - Where the new converted object is returned
- *
- * RETURN:      Status. AE_OK if conversion was successful.
- *
- * DESCRIPTION: Attempt to convert a Buffer object to a Package. Each byte of
- *              the buffer is converted to a single integer package element.
- *
- ******************************************************************************/
-
-static acpi_status
-acpi_ns_convert_to_package(union acpi_operand_object *original_object,
-                          union acpi_operand_object **return_object)
-{
-       union acpi_operand_object *new_object;
-       union acpi_operand_object **elements;
-       u32 length;
-       u8 *buffer;
-
-       switch (original_object->common.type) {
-       case ACPI_TYPE_BUFFER:
-
-               /* Buffer-to-Package conversion */
-
-               length = original_object->buffer.length;
-               new_object = acpi_ut_create_package_object(length);
-               if (!new_object) {
-                       return (AE_NO_MEMORY);
-               }
-
-               /* Convert each buffer byte to an integer package element */
-
-               elements = new_object->package.elements;
-               buffer = original_object->buffer.pointer;
-
-               while (length--) {
-                       *elements =
-                           acpi_ut_create_integer_object((u64) *buffer);
-                       if (!*elements) {
-                               acpi_ut_remove_reference(new_object);
-                               return (AE_NO_MEMORY);
-                       }
-                       elements++;
-                       buffer++;
-               }
-               break;
-
-       default:
-               return (AE_AML_OPERAND_TYPE);
-       }
-
-       *return_object = new_object;
-       return (AE_OK);
-}
-
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ns_repair_null_element
@@ -677,55 +633,56 @@ acpi_ns_remove_null_elements(struct acpi_predefined_data *data,
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_ns_repair_package_list
+ * FUNCTION:    acpi_ns_wrap_with_package
  *
  * PARAMETERS:  Data                - Pointer to validation data structure
- *              obj_desc_ptr        - Pointer to the object to repair. The new
- *                                    package object is returned here,
- *                                    overwriting the old object.
+ *              original_object     - Pointer to the object to repair.
+ *              obj_desc_ptr        - The new package object is returned here
  *
  * RETURN:      Status, new object in *obj_desc_ptr
  *
- * DESCRIPTION: Repair a common problem with objects that are defined to return
- *              a variable-length Package of Packages. If the variable-length
- *              is one, some BIOS code mistakenly simply declares a single
- *              Package instead of a Package with one sub-Package. This
- *              function attempts to repair this error by wrapping a Package
- *              object around the original Package, creating the correct
- *              Package with one sub-Package.
+ * DESCRIPTION: Repair a common problem with objects that are defined to
+ *              return a variable-length Package of sub-objects. If there is
+ *              only one sub-object, some BIOS code mistakenly simply declares
+ *              the single object instead of a Package with one sub-object.
+ *              This function attempts to repair this error by wrapping a
+ *              Package object around the original object, creating the
+ *              correct and expected Package with one sub-object.
  *
  *              Names that can be repaired in this manner include:
- *              _ALR, _CSD, _HPX, _MLS, _PRT, _PSS, _TRT, TSS
+ *              _ALR, _CSD, _HPX, _MLS, _PLD, _PRT, _PSS, _TRT, _TSS,
+ *              _BCL, _DOD, _FIX, _Sx
  *
  ******************************************************************************/
 
 acpi_status
-acpi_ns_repair_package_list(struct acpi_predefined_data *data,
-                           union acpi_operand_object **obj_desc_ptr)
+acpi_ns_wrap_with_package(struct acpi_predefined_data *data,
+                         union acpi_operand_object *original_object,
+                         union acpi_operand_object **obj_desc_ptr)
 {
        union acpi_operand_object *pkg_obj_desc;
 
-       ACPI_FUNCTION_NAME(ns_repair_package_list);
+       ACPI_FUNCTION_NAME(ns_wrap_with_package);
 
        /*
         * Create the new outer package and populate it. The new package will
-        * have a single element, the lone subpackage.
+        * have a single element, the lone sub-object.
         */
        pkg_obj_desc = acpi_ut_create_package_object(1);
        if (!pkg_obj_desc) {
                return (AE_NO_MEMORY);
        }
 
-       pkg_obj_desc->package.elements[0] = *obj_desc_ptr;
+       pkg_obj_desc->package.elements[0] = original_object;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+                         "%s: Wrapped %s with expected Package object\n",
+                         data->pathname,
+                         acpi_ut_get_object_type_name(original_object)));
 
        /* Return the new object in the object pointer */
 
        *obj_desc_ptr = pkg_obj_desc;
-       data->flags |= ACPI_OBJECT_REPAIRED;
-
-       ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
-                         "%s: Repaired incorrectly formed Package\n",
-                         data->pathname));
-
+       data->flags |= ACPI_OBJECT_REPAIRED | ACPI_OBJECT_WRAPPED;
        return (AE_OK);
 }
index a535b7afda5cc3daf2cb80a28fd03fa18c22a6df..75113759f69ddf22a07ce57b4f6117b6e1b4ca70 100644 (file)
@@ -341,7 +341,7 @@ acpi_status acpi_ns_build_internal_name(struct acpi_namestring_info *info)
 
                if (!acpi_ns_valid_path_separator(*external_name) &&
                    (*external_name != 0)) {
-                       return_ACPI_STATUS(AE_BAD_PARAMETER);
+                       return_ACPI_STATUS(AE_BAD_PATHNAME);
                }
 
                /* Move on the next segment */
index c5d870406f4126adf7ce80ffa0635d175c1a772d..4c9c760db4a469f2db6291ac2437f9308002c15f 100644 (file)
@@ -363,10 +363,6 @@ static void acpi_tb_convert_fadt(void)
        u32 address32;
        u32 i;
 
-       /* Update the local FADT table header length */
-
-       acpi_gbl_FADT.header.length = sizeof(struct acpi_table_fadt);
-
        /*
         * Expand the 32-bit FACS and DSDT addresses to 64-bit as necessary.
         * Later code will always use the X 64-bit field. Also, check for an
@@ -408,6 +404,10 @@ static void acpi_tb_convert_fadt(void)
                acpi_gbl_FADT.boot_flags = 0;
        }
 
+       /* Update the local FADT table header length */
+
+       acpi_gbl_FADT.header.length = sizeof(struct acpi_table_fadt);
+
        /*
         * Expand the ACPI 1.0 32-bit addresses to the ACPI 2.0 64-bit "X"
         * generic address structures as necessary. Later code will always use
index 1aecf7baa4e0c2f2a0f35c7ebc4ecd8bbbe161c7..c03500b4cc7ac999bc757205fd765708745220a9 100644 (file)
@@ -114,7 +114,6 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index)
 {
        u32 i;
        acpi_status status = AE_OK;
-       struct acpi_table_header *override_table = NULL;
 
        ACPI_FUNCTION_TRACE(tb_add_table);
 
@@ -224,25 +223,10 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index)
        /*
         * ACPI Table Override:
         * Allow the host to override dynamically loaded tables.
+        * NOTE: the table is fully mapped at this point, and the mapping will
+        * be deleted by tb_table_override if the table is actually overridden.
         */
-       status = acpi_os_table_override(table_desc->pointer, &override_table);
-       if (ACPI_SUCCESS(status) && override_table) {
-               ACPI_INFO((AE_INFO,
-                          "%4.4s @ 0x%p Table override, replaced with:",
-                          table_desc->pointer->signature,
-                          ACPI_CAST_PTR(void, table_desc->address)));
-
-               /* We can delete the table that was passed as a parameter */
-
-               acpi_tb_delete_table(table_desc);
-
-               /* Setup descriptor for the new table */
-
-               table_desc->address = ACPI_PTR_TO_PHYSADDR(override_table);
-               table_desc->pointer = override_table;
-               table_desc->length = override_table->length;
-               table_desc->flags = ACPI_TABLE_ORIGIN_OVERRIDE;
-       }
+       (void)acpi_tb_table_override(table_desc->pointer, table_desc);
 
        /* Add the table to the global root table list */
 
@@ -261,6 +245,95 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index)
        return_ACPI_STATUS(status);
 }
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_table_override
+ *
+ * PARAMETERS:  table_header        - Header for the original table
+ *              table_desc          - Table descriptor initialized for the
+ *                                    original table. May or may not be mapped.
+ *
+ * RETURN:      Pointer to the entire new table. NULL if table not overridden.
+ *              If overridden, installs the new table within the input table
+ *              descriptor.
+ *
+ * DESCRIPTION: Attempt table override by calling the OSL override functions.
+ *              Note: If the table is overridden, then the entire new table
+ *              is mapped and returned by this function.
+ *
+ ******************************************************************************/
+
+struct acpi_table_header *acpi_tb_table_override(struct acpi_table_header
+                                                *table_header,
+                                                struct acpi_table_desc
+                                                *table_desc)
+{
+       acpi_status status;
+       struct acpi_table_header *new_table = NULL;
+       acpi_physical_address new_address = 0;
+       u32 new_table_length = 0;
+       u8 new_flags;
+       char *override_type;
+
+       /* (1) Attempt logical override (returns a logical address) */
+
+       status = acpi_os_table_override(table_header, &new_table);
+       if (ACPI_SUCCESS(status) && new_table) {
+               new_address = ACPI_PTR_TO_PHYSADDR(new_table);
+               new_table_length = new_table->length;
+               new_flags = ACPI_TABLE_ORIGIN_OVERRIDE;
+               override_type = "Logical";
+               goto finish_override;
+       }
+
+       /* (2) Attempt physical override (returns a physical address) */
+
+       status = acpi_os_physical_table_override(table_header,
+                                                &new_address,
+                                                &new_table_length);
+       if (ACPI_SUCCESS(status) && new_address && new_table_length) {
+
+               /* Map the entire new table */
+
+               new_table = acpi_os_map_memory(new_address, new_table_length);
+               if (!new_table) {
+                       ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY,
+                                       "%4.4s %p Attempted physical table override failed",
+                                       table_header->signature,
+                                       ACPI_CAST_PTR(void,
+                                                     table_desc->address)));
+                       return (NULL);
+               }
+
+               override_type = "Physical";
+               new_flags = ACPI_TABLE_ORIGIN_MAPPED;
+               goto finish_override;
+       }
+
+       return (NULL);          /* There was no override */
+
+      finish_override:
+
+       ACPI_INFO((AE_INFO,
+                  "%4.4s %p %s table override, new table: %p",
+                  table_header->signature,
+                  ACPI_CAST_PTR(void, table_desc->address),
+                  override_type, new_table));
+
+       /* We can now unmap/delete the original table (if fully mapped) */
+
+       acpi_tb_delete_table(table_desc);
+
+       /* Setup descriptor for the new table */
+
+       table_desc->address = new_address;
+       table_desc->pointer = new_table;
+       table_desc->length = new_table_length;
+       table_desc->flags = new_flags;
+
+       return (new_table);
+}
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_tb_resize_root_table_list
@@ -396,7 +469,11 @@ void acpi_tb_delete_table(struct acpi_table_desc *table_desc)
        case ACPI_TABLE_ORIGIN_ALLOCATED:
                ACPI_FREE(table_desc->pointer);
                break;
-       default:;
+
+               /* Not mapped or allocated, there is nothing we can do */
+
+       default:
+               return;
        }
 
        table_desc->pointer = NULL;
index 09ca39e143373c7dfeb11efc63f01540e62d3edb..0a706cac37de0736c6c758223ba242a16412c6c8 100644 (file)
@@ -118,6 +118,7 @@ acpi_tb_check_xsdt(acpi_physical_address address)
                return AE_OK;
 }
 
+#if (!ACPI_REDUCED_HARDWARE)
 /*******************************************************************************
  *
  * FUNCTION:    acpi_tb_initialize_facs
@@ -148,6 +149,7 @@ acpi_status acpi_tb_initialize_facs(void)
                                                                &acpi_gbl_FACS));
        return status;
 }
+#endif                         /* !ACPI_REDUCED_HARDWARE */
 
 /*******************************************************************************
  *
@@ -444,7 +446,7 @@ struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index)
  * RETURN:      None
  *
  * DESCRIPTION: Install an ACPI table into the global data structure. The
- *              table override mechanism is implemented here to allow the host
+ *              table override mechanism is called to allow the host
  *              OS to replace any table before it is installed in the root
  *              table array.
  *
@@ -454,11 +456,9 @@ void
 acpi_tb_install_table(acpi_physical_address address,
                      char *signature, u32 table_index)
 {
-       u8 flags;
-       acpi_status status;
-       struct acpi_table_header *table_to_install;
-       struct acpi_table_header *mapped_table;
-       struct acpi_table_header *override_table = NULL;
+       struct acpi_table_header *table;
+       struct acpi_table_header *final_table;
+       struct acpi_table_desc *table_desc;
 
        if (!address) {
                ACPI_ERROR((AE_INFO,
@@ -469,69 +469,78 @@ acpi_tb_install_table(acpi_physical_address address,
 
        /* Map just the table header */
 
-       mapped_table =
-           acpi_os_map_memory(address, sizeof(struct acpi_table_header));
-       if (!mapped_table) {
+       table = acpi_os_map_memory(address, sizeof(struct acpi_table_header));
+       if (!table) {
+               ACPI_ERROR((AE_INFO,
+                           "Could not map memory for table [%s] at %p",
+                           signature, ACPI_CAST_PTR(void, address)));
                return;
        }
 
        /* If a particular signature is expected (DSDT/FACS), it must match */
 
-       if (signature && !ACPI_COMPARE_NAME(mapped_table->signature, signature)) {
+       if (signature && !ACPI_COMPARE_NAME(table->signature, signature)) {
                ACPI_ERROR((AE_INFO,
                            "Invalid signature 0x%X for ACPI table, expected [%s]",
-                           *ACPI_CAST_PTR(u32, mapped_table->signature),
-                           signature));
+                           *ACPI_CAST_PTR(u32, table->signature), signature));
                goto unmap_and_exit;
        }
 
+       /*
+        * Initialize the table entry. Set the pointer to NULL, since the
+        * table is not fully mapped at this time.
+        */
+       table_desc = &acpi_gbl_root_table_list.tables[table_index];
+
+       table_desc->address = address;
+       table_desc->pointer = NULL;
+       table_desc->length = table->length;
+       table_desc->flags = ACPI_TABLE_ORIGIN_MAPPED;
+       ACPI_MOVE_32_TO_32(table_desc->signature.ascii, table->signature);
+
        /*
         * ACPI Table Override:
         *
         * Before we install the table, let the host OS override it with a new
         * one if desired. Any table within the RSDT/XSDT can be replaced,
         * including the DSDT which is pointed to by the FADT.
+        *
+        * NOTE: If the table is overridden, then final_table will contain a
+        * mapped pointer to the full new table. If the table is not overridden,
+        * or if there has been a physical override, then the table will be
+        * fully mapped later (in verify table). In any case, we must
+        * unmap the header that was mapped above.
         */
-       status = acpi_os_table_override(mapped_table, &override_table);
-       if (ACPI_SUCCESS(status) && override_table) {
-               ACPI_INFO((AE_INFO,
-                          "%4.4s @ 0x%p Table override, replaced with:",
-                          mapped_table->signature, ACPI_CAST_PTR(void,
-                                                                 address)));
-
-               acpi_gbl_root_table_list.tables[table_index].pointer =
-                   override_table;
-               address = ACPI_PTR_TO_PHYSADDR(override_table);
-
-               table_to_install = override_table;
-               flags = ACPI_TABLE_ORIGIN_OVERRIDE;
-       } else {
-               table_to_install = mapped_table;
-               flags = ACPI_TABLE_ORIGIN_MAPPED;
+       final_table = acpi_tb_table_override(table, table_desc);
+       if (!final_table) {
+               final_table = table;    /* There was no override */
        }
 
-       /* Initialize the table entry */
+       acpi_tb_print_table_header(table_desc->address, final_table);
 
-       acpi_gbl_root_table_list.tables[table_index].address = address;
-       acpi_gbl_root_table_list.tables[table_index].length =
-           table_to_install->length;
-       acpi_gbl_root_table_list.tables[table_index].flags = flags;
-
-       ACPI_MOVE_32_TO_32(&
-                          (acpi_gbl_root_table_list.tables[table_index].
-                           signature), table_to_install->signature);
-
-       acpi_tb_print_table_header(address, table_to_install);
+       /* Set the global integer width (based upon revision of the DSDT) */
 
        if (table_index == ACPI_TABLE_INDEX_DSDT) {
+               acpi_ut_set_integer_width(final_table->revision);
+       }
 
-               /* Global integer width is based upon revision of the DSDT */
-
-               acpi_ut_set_integer_width(table_to_install->revision);
+       /*
+        * If we have a physical override during this early loading of the ACPI
+        * tables, unmap the table for now. It will be mapped again later when
+        * it is actually used. This supports very early loading of ACPI tables,
+        * before virtual memory is fully initialized and running within the
+        * host OS. Note: A logical override has the ACPI_TABLE_ORIGIN_OVERRIDE
+        * flag set and will not be deleted below.
+        */
+       if (final_table != table) {
+               acpi_tb_delete_table(table_desc);
        }
 
       unmap_and_exit:
-       acpi_os_unmap_memory(mapped_table, sizeof(struct acpi_table_header));
+
+       /* Always unmap the table header that we mapped above */
+
+       acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
 }
 
 /*******************************************************************************
index d42ede5260c77cac6f9b947ec625c252ebe027bc..684849949bf3e64550e2a0cbb6e0f42d4525933b 100644 (file)
@@ -497,19 +497,20 @@ char *acpi_ut_get_mutex_name(u32 mutex_id)
 
 /* Names for Notify() values, used for debug output */
 
-static const char *acpi_gbl_notify_value_names[] = {
-       "Bus Check",
-       "Device Check",
-       "Device Wake",
-       "Eject Request",
-       "Device Check Light",
-       "Frequency Mismatch",
-       "Bus Mode Mismatch",
-       "Power Fault",
-       "Capabilities Check",
-       "Device PLD Check",
-       "Reserved",
-       "System Locality Update"
+static const char *acpi_gbl_notify_value_names[ACPI_NOTIFY_MAX + 1] = {
+       /* 00 */ "Bus Check",
+       /* 01 */ "Device Check",
+       /* 02 */ "Device Wake",
+       /* 03 */ "Eject Request",
+       /* 04 */ "Device Check Light",
+       /* 05 */ "Frequency Mismatch",
+       /* 06 */ "Bus Mode Mismatch",
+       /* 07 */ "Power Fault",
+       /* 08 */ "Capabilities Check",
+       /* 09 */ "Device PLD Check",
+       /* 10 */ "Reserved",
+       /* 11 */ "System Locality Update",
+       /* 12 */ "Shutdown Request"
 };
 
 const char *acpi_ut_get_notify_name(u32 notify_value)
@@ -519,9 +520,10 @@ const char *acpi_ut_get_notify_name(u32 notify_value)
                return (acpi_gbl_notify_value_names[notify_value]);
        } else if (notify_value <= ACPI_MAX_SYS_NOTIFY) {
                return ("Reserved");
-       } else {                /* Greater or equal to 0x80 */
-
-               return ("**Device Specific**");
+       } else if (notify_value <= ACPI_MAX_DEVICE_SPECIFIC_NOTIFY) {
+               return ("Device Specific");
+       } else {
+               return ("Hardware Specific");
        }
 }
 #endif
index 4153584cf526a81bb3ab99bd3f5f59a37fb19f42..90f53b42eca9fcdfe2cda7d88a47cdb076dfc02e 100644 (file)
@@ -140,6 +140,7 @@ const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = {
        {NULL, ACPI_TYPE_ANY, NULL}
 };
 
+#if (!ACPI_REDUCED_HARDWARE)
 /******************************************************************************
  *
  * Event and Hardware globals
@@ -236,6 +237,7 @@ struct acpi_fixed_event_info acpi_gbl_fixed_event_info[ACPI_NUM_FIXED_EVENTS] =
                                        ACPI_BITMASK_RT_CLOCK_STATUS,
                                        ACPI_BITMASK_RT_CLOCK_ENABLE},
 };
+#endif                         /* !ACPI_REDUCED_HARDWARE */
 
 /*******************************************************************************
  *
@@ -286,6 +288,8 @@ acpi_status acpi_ut_init_globals(void)
 
        acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS - 1] = 0x80000000;
 
+#if (!ACPI_REDUCED_HARDWARE)
+
        /* GPE support */
 
        acpi_gbl_gpe_xrupt_list_head = NULL;
@@ -294,6 +298,10 @@ acpi_status acpi_ut_init_globals(void)
        acpi_current_gpe_count = 0;
        acpi_gbl_all_gpes_initialized = FALSE;
 
+       acpi_gbl_global_event_handler = NULL;
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
+
        /* Global handlers */
 
        acpi_gbl_system_notify.handler = NULL;
@@ -302,7 +310,6 @@ acpi_status acpi_ut_init_globals(void)
        acpi_gbl_init_handler = NULL;
        acpi_gbl_table_handler = NULL;
        acpi_gbl_interface_handler = NULL;
-       acpi_gbl_global_event_handler = NULL;
 
        /* Global Lock support */
 
index 8359c0c5dc9830456ec2605f2bcc9114cd4a25cc..246798e4c938d4f537d2f17290366a5b99f045a7 100644 (file)
@@ -53,27 +53,35 @@ ACPI_MODULE_NAME("utinit")
 /* Local prototypes */
 static void acpi_ut_terminate(void);
 
+#if (!ACPI_REDUCED_HARDWARE)
+
+static void acpi_ut_free_gpe_lists(void);
+
+#else
+
+#define acpi_ut_free_gpe_lists()
+#endif                         /* !ACPI_REDUCED_HARDWARE */
+
+#if (!ACPI_REDUCED_HARDWARE)
 /******************************************************************************
  *
- * FUNCTION:    acpi_ut_terminate
+ * FUNCTION:    acpi_ut_free_gpe_lists
  *
  * PARAMETERS:  none
  *
  * RETURN:      none
  *
- * DESCRIPTION: Free global memory
+ * DESCRIPTION: Free global GPE lists
  *
  ******************************************************************************/
 
-static void acpi_ut_terminate(void)
+static void acpi_ut_free_gpe_lists(void)
 {
        struct acpi_gpe_block_info *gpe_block;
        struct acpi_gpe_block_info *next_gpe_block;
        struct acpi_gpe_xrupt_info *gpe_xrupt_info;
        struct acpi_gpe_xrupt_info *next_gpe_xrupt_info;
 
-       ACPI_FUNCTION_TRACE(ut_terminate);
-
        /* Free global GPE blocks and related info structures */
 
        gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;
@@ -91,7 +99,26 @@ static void acpi_ut_terminate(void)
                ACPI_FREE(gpe_xrupt_info);
                gpe_xrupt_info = next_gpe_xrupt_info;
        }
+}
+#endif                         /* !ACPI_REDUCED_HARDWARE */
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ut_terminate
+ *
+ * PARAMETERS:  none
+ *
+ * RETURN:      none
+ *
+ * DESCRIPTION: Free global memory
+ *
+ ******************************************************************************/
+
+static void acpi_ut_terminate(void)
+{
+       ACPI_FUNCTION_TRACE(ut_terminate);
 
+       acpi_ut_free_gpe_lists();
        acpi_ut_delete_address_lists();
        return_VOID;
 }
index 644e8c8ebc4b30c6ea3ca607708e3478c806c4bc..afa94f51ff0b441911eb536b5b946b356fab95c1 100644 (file)
@@ -145,6 +145,8 @@ acpi_status acpi_enable_subsystem(u32 flags)
 
        ACPI_FUNCTION_TRACE(acpi_enable_subsystem);
 
+#if (!ACPI_REDUCED_HARDWARE)
+
        /* Enable ACPI mode */
 
        if (!(flags & ACPI_NO_ACPI_ENABLE)) {
@@ -169,6 +171,7 @@ acpi_status acpi_enable_subsystem(u32 flags)
                ACPI_WARNING((AE_INFO, "Could not map the FACS table"));
                return_ACPI_STATUS(status);
        }
+#endif                         /* !ACPI_REDUCED_HARDWARE */
 
        /*
         * Install the default op_region handlers. These are installed unless
@@ -184,7 +187,7 @@ acpi_status acpi_enable_subsystem(u32 flags)
                        return_ACPI_STATUS(status);
                }
        }
-
+#if (!ACPI_REDUCED_HARDWARE)
        /*
         * Initialize ACPI Event handling (Fixed and General Purpose)
         *
@@ -220,6 +223,7 @@ acpi_status acpi_enable_subsystem(u32 flags)
                        return_ACPI_STATUS(status);
                }
        }
+#endif                         /* !ACPI_REDUCED_HARDWARE */
 
        return_ACPI_STATUS(status);
 }
index e5d53b7ddc7e0eb024f7cdc6863619bd2c3dc0a2..5577762daee1d7d22a8dbf49a27cf02ead2e54a4 100644 (file)
@@ -558,33 +558,48 @@ void apei_resources_release(struct apei_resources *resources)
 }
 EXPORT_SYMBOL_GPL(apei_resources_release);
 
-static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr)
+static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
+                               u32 *access_bit_width)
 {
-       u32 width, space_id;
+       u32 bit_width, bit_offset, access_size_code, space_id;
 
-       width = reg->bit_width;
+       bit_width = reg->bit_width;
+       bit_offset = reg->bit_offset;
+       access_size_code = reg->access_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);
+                          "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n",
+                          *paddr, bit_width, bit_offset, access_size_code,
+                          space_id);
                return -EINVAL;
        }
 
-       if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
+       if (access_size_code < 1 || access_size_code > 4) {
                pr_warning(FW_BUG APEI_PFX
-                          "Invalid bit width in GAR [0x%llx/%u/%u]\n",
-                          *paddr, width, space_id);
+                          "Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n",
+                          *paddr, bit_width, bit_offset, access_size_code,
+                          space_id);
+               return -EINVAL;
+       }
+       *access_bit_width = 1UL << (access_size_code + 2);
+
+       if ((bit_width + bit_offset) > *access_bit_width) {
+               pr_warning(FW_BUG APEI_PFX
+                          "Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n",
+                          *paddr, bit_width, bit_offset, access_size_code,
+                          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);
+                          "Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n",
+                          *paddr, bit_width, bit_offset, access_size_code,
+                          space_id);
                return -EINVAL;
        }
 
@@ -595,23 +610,25 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr)
 int apei_read(u64 *val, struct acpi_generic_address *reg)
 {
        int rc;
+       u32 access_bit_width;
        u64 address;
        acpi_status status;
 
-       rc = apei_check_gar(reg, &address);
+       rc = apei_check_gar(reg, &address, &access_bit_width);
        if (rc)
                return rc;
 
        *val = 0;
        switch(reg->space_id) {
        case ACPI_ADR_SPACE_SYSTEM_MEMORY:
-               status = acpi_os_read_memory64((acpi_physical_address)
-                                            address, val, reg->bit_width);
+               status = acpi_os_read_memory((acpi_physical_address) address,
+                                              val, access_bit_width);
                if (ACPI_FAILURE(status))
                        return -EIO;
                break;
        case ACPI_ADR_SPACE_SYSTEM_IO:
-               status = acpi_os_read_port(address, (u32 *)val, reg->bit_width);
+               status = acpi_os_read_port(address, (u32 *)val,
+                                          access_bit_width);
                if (ACPI_FAILURE(status))
                        return -EIO;
                break;
@@ -627,22 +644,23 @@ EXPORT_SYMBOL_GPL(apei_read);
 int apei_write(u64 val, struct acpi_generic_address *reg)
 {
        int rc;
+       u32 access_bit_width;
        u64 address;
        acpi_status status;
 
-       rc = apei_check_gar(reg, &address);
+       rc = apei_check_gar(reg, &address, &access_bit_width);
        if (rc)
                return rc;
 
        switch (reg->space_id) {
        case ACPI_ADR_SPACE_SYSTEM_MEMORY:
-               status = acpi_os_write_memory64((acpi_physical_address)
-                                             address, val, reg->bit_width);
+               status = acpi_os_write_memory((acpi_physical_address) address,
+                                               val, access_bit_width);
                if (ACPI_FAILURE(status))
                        return -EIO;
                break;
        case ACPI_ADR_SPACE_SYSTEM_IO:
-               status = acpi_os_write_port(address, val, reg->bit_width);
+               status = acpi_os_write_port(address, val, access_bit_width);
                if (ACPI_FAILURE(status))
                        return -EIO;
                break;
@@ -661,23 +679,24 @@ static int collect_res_callback(struct apei_exec_context *ctx,
        struct apei_resources *resources = data;
        struct acpi_generic_address *reg = &entry->register_region;
        u8 ins = entry->instruction;
+       u32 access_bit_width;
        u64 paddr;
        int rc;
 
        if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER))
                return 0;
 
-       rc = apei_check_gar(reg, &paddr);
+       rc = apei_check_gar(reg, &paddr, &access_bit_width);
        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);
+                                   access_bit_width / 8);
        case ACPI_ADR_SPACE_SYSTEM_IO:
                return apei_res_add(&resources->ioport, paddr,
-                                   reg->bit_width / 8);
+                                   access_bit_width / 8);
        default:
                return -EINVAL;
        }
index 5d4189464d63155a300c4f7c66f9abb9d0cb11ec..e6defd86b42454e98d2828abf2d99240d9e68b18 100644 (file)
@@ -362,6 +362,7 @@ void apei_estatus_print(const char *pfx,
                gedata_len = gdata->error_data_length;
                apei_estatus_print_section(pfx, gdata, sec_no);
                data_len -= gedata_len + sizeof(*gdata);
+               gdata = (void *)(gdata + 1) + gedata_len;
                sec_no++;
        }
 }
@@ -396,6 +397,7 @@ int apei_estatus_check(const struct acpi_hest_generic_status *estatus)
                if (gedata_len > data_len - sizeof(*gdata))
                        return -EINVAL;
                data_len -= gedata_len + sizeof(*gdata);
+               gdata = (void *)(gdata + 1) + gedata_len;
        }
        if (data_len)
                return -EINVAL;
index 4ca087dd5f4fceb797e1272912b06f082af7549d..8e1793649ec0e992692a465011ff18c8e25e2b77 100644 (file)
@@ -74,6 +74,8 @@ struct vendor_error_type_extension {
        u8      reserved[3];
 };
 
+static u32 notrigger;
+
 static u32 vendor_flags;
 static struct debugfs_blob_wrapper vendor_blob;
 static char vendor_dev[64];
@@ -238,7 +240,7 @@ static void *einj_get_parameter_address(void)
                        return v5param;
                }
        }
-       if (paddrv4) {
+       if (param_extension && paddrv4) {
                struct einj_parameter *v4param;
 
                v4param = acpi_os_map_memory(paddrv4, sizeof(*v4param));
@@ -496,9 +498,11 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
        if (rc)
                return rc;
        trigger_paddr = apei_exec_ctx_get_output(&ctx);
-       rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
-       if (rc)
-               return rc;
+       if (notrigger == 0) {
+               rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
+               if (rc)
+                       return rc;
+       }
        rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
 
        return rc;
@@ -700,6 +704,11 @@ static int __init einj_init(void)
                                            einj_debug_dir, &error_param2);
                if (!fentry)
                        goto err_unmap;
+
+               fentry = debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR,
+                                           einj_debug_dir, &notrigger);
+               if (!fentry)
+                       goto err_unmap;
        }
 
        if (vendor_dev[0]) {
index eb9fab5b96e4d3dca071e11e8dbdec2405b23508..e4d9d24eb73dda047655ac018ed45b6259effedb 100644 (file)
@@ -917,7 +917,7 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
 {
        if ((erst_tab->header_length !=
             (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header)))
-           && (erst_tab->header_length != sizeof(struct acpi_table_einj)))
+           && (erst_tab->header_length != sizeof(struct acpi_table_erst)))
                return -EINVAL;
        if (erst_tab->header.length < sizeof(struct acpi_table_erst))
                return -EINVAL;
diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c
new file mode 100644 (file)
index 0000000..8cf6c46
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2012 Red Hat, Inc <mjg@redhat.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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+
+static struct acpi_table_bgrt *bgrt_tab;
+static struct kobject *bgrt_kobj;
+
+struct bmp_header {
+       u16 id;
+       u32 size;
+} __attribute ((packed));
+
+static struct bmp_header bmp_header;
+
+static ssize_t show_version(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->version);
+}
+static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
+
+static ssize_t show_status(struct device *dev,
+                          struct device_attribute *attr, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->status);
+}
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static ssize_t show_type(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_type);
+}
+static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
+
+static ssize_t show_xoffset(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_x);
+}
+static DEVICE_ATTR(xoffset, S_IRUGO, show_xoffset, NULL);
+
+static ssize_t show_yoffset(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_y);
+}
+static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL);
+
+static ssize_t show_image(struct file *file, struct kobject *kobj,
+              struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+{
+       int size = attr->size;
+       void __iomem *image = attr->private;
+
+       if (off >= size) {
+               count = 0;
+       } else {
+               if (off + count > size)
+                       count = size - off;
+
+               memcpy_fromio(buf, image+off, count);
+       }
+
+       return count;
+}
+
+static struct bin_attribute image_attr = {
+       .attr = {
+               .name = "image",
+               .mode = S_IRUGO,
+       },
+       .read = show_image,
+};
+
+static struct attribute *bgrt_attributes[] = {
+       &dev_attr_version.attr,
+       &dev_attr_status.attr,
+       &dev_attr_type.attr,
+       &dev_attr_xoffset.attr,
+       &dev_attr_yoffset.attr,
+       NULL,
+};
+
+static struct attribute_group bgrt_attribute_group = {
+       .attrs = bgrt_attributes,
+};
+
+static int __init bgrt_init(void)
+{
+       acpi_status status;
+       int ret;
+       void __iomem *bgrt;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       status = acpi_get_table("BGRT", 0,
+                               (struct acpi_table_header **)&bgrt_tab);
+
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       sysfs_bin_attr_init(&image_attr);
+
+       bgrt = ioremap(bgrt_tab->image_address, sizeof(struct bmp_header));
+
+       if (!bgrt) {
+               ret = -EINVAL;
+               goto out_err;
+       }
+
+       memcpy_fromio(&bmp_header, bgrt, sizeof(bmp_header));
+       image_attr.size = bmp_header.size;
+       iounmap(bgrt);
+
+       image_attr.private = ioremap(bgrt_tab->image_address, image_attr.size);
+
+       if (!image_attr.private) {
+               ret = -EINVAL;
+               goto out_err;
+       }
+
+
+       bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj);
+       if (!bgrt_kobj) {
+               ret = -EINVAL;
+               goto out_iounmap;
+       }
+
+       ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group);
+       if (ret)
+               goto out_kobject;
+
+       ret = sysfs_create_bin_file(bgrt_kobj, &image_attr);
+       if (ret)
+               goto out_group;
+
+       return 0;
+
+out_group:
+       sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
+out_kobject:
+       kobject_put(bgrt_kobj);
+out_iounmap:
+       iounmap(image_attr.private);
+out_err:
+       return ret;
+}
+
+static void __exit bgrt_exit(void)
+{
+       iounmap(image_attr.private);
+       sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
+       sysfs_remove_bin_file(bgrt_kobj, &image_attr);
+}
+
+module_init(bgrt_init);
+module_exit(bgrt_exit);
+
+MODULE_AUTHOR("Matthew Garrett");
+MODULE_DESCRIPTION("BGRT boot graphic support");
+MODULE_LICENSE("GPL");
index 9ecec98bc76e9aa030c3c5c0c060ba283e1dcf64..3263b68cdfa3ea3404d8eb647fe317f28bc1c8ce 100644 (file)
@@ -1010,6 +1010,7 @@ static int __init acpi_bus_init(void)
 }
 
 struct kobject *acpi_kobj;
+EXPORT_SYMBOL_GPL(acpi_kobj);
 
 static int __init acpi_init(void)
 {
index e37615f310d731a2f3837304b3d7f18a039bb5e4..7edaccce66402b75b1b32228bfc7cebc000066c4 100644 (file)
@@ -822,10 +822,10 @@ static int acpi_ec_add(struct acpi_device *device)
                first_ec = ec;
        device->driver_data = ec;
 
-       WARN(!request_region(ec->data_addr, 1, "EC data"),
-            "Could not request EC data io port 0x%lx", ec->data_addr);
-       WARN(!request_region(ec->command_addr, 1, "EC cmd"),
-            "Could not request EC cmd io port 0x%lx", ec->command_addr);
+       ret = !!request_region(ec->data_addr, 1, "EC data");
+       WARN(!ret, "Could not request EC data io port 0x%lx", ec->data_addr);
+       ret = !!request_region(ec->command_addr, 1, "EC cmd");
+       WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr);
 
        pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n",
                          ec->gpe, ec->command_addr, ec->data_addr);
index 7a2035fa8c713d660a697d0d49e0a430476ed9fa..266bc58ce0ce165eb4c1a6bf707c7af5d5080c36 100644 (file)
@@ -95,8 +95,8 @@ static int suspend_nvs_register(unsigned long start, unsigned long size)
 {
        struct nvs_page *entry, *next;
 
-       pr_info("PM: Registering ACPI NVS region at %lx (%ld bytes)\n",
-               start, size);
+       pr_info("PM: Registering ACPI NVS region [mem %#010lx-%#010lx] (%ld bytes)\n",
+               start, start + size - 1, size);
 
        while (size > 0) {
                unsigned int nr_bytes;
index 412a1e04a9226a84970654c433597455cb53f0ca..ba14fb93c92946097bafc866e316c79234ab02f3 100644 (file)
@@ -77,6 +77,9 @@ EXPORT_SYMBOL(acpi_in_debugger);
 extern char line_buf[80];
 #endif                         /*ENABLE_DEBUGGER */
 
+static int (*__acpi_os_prepare_sleep)(u8 sleep_state, u32 pm1a_ctrl,
+                                     u32 pm1b_ctrl);
+
 static acpi_osd_handler acpi_irq_handler;
 static void *acpi_irq_context;
 static struct workqueue_struct *kacpid_wq;
@@ -347,7 +350,7 @@ static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr)
        unsigned long pfn;
 
        pfn = pg_off >> PAGE_SHIFT;
-       if (page_is_ram(pfn))
+       if (should_use_kmap(pfn))
                kunmap(pfn_to_page(pfn));
        else
                iounmap(vaddr);
@@ -554,6 +557,15 @@ acpi_os_table_override(struct acpi_table_header * existing_table,
        return AE_OK;
 }
 
+acpi_status
+acpi_os_physical_table_override(struct acpi_table_header *existing_table,
+                               acpi_physical_address * new_address,
+                               u32 *new_table_length)
+{
+       return AE_SUPPORT;
+}
+
+
 static irqreturn_t acpi_irq(int irq, void *dev_id)
 {
        u32 handled;
@@ -595,7 +607,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
 
        acpi_irq_handler = handler;
        acpi_irq_context = context;
-       if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) {
+       if (request_threaded_irq(irq, NULL, acpi_irq, IRQF_SHARED, "acpi",
+                                acpi_irq)) {
                printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq);
                acpi_irq_handler = NULL;
                return AE_NOT_ACQUIRED;
@@ -699,49 +712,6 @@ acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width)
 
 EXPORT_SYMBOL(acpi_os_write_port);
 
-acpi_status
-acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width)
-{
-       void __iomem *virt_addr;
-       unsigned int size = width / 8;
-       bool unmap = false;
-       u32 dummy;
-
-       rcu_read_lock();
-       virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
-       if (!virt_addr) {
-               rcu_read_unlock();
-               virt_addr = acpi_os_ioremap(phys_addr, size);
-               if (!virt_addr)
-                       return AE_BAD_ADDRESS;
-               unmap = true;
-       }
-
-       if (!value)
-               value = &dummy;
-
-       switch (width) {
-       case 8:
-               *(u8 *) value = readb(virt_addr);
-               break;
-       case 16:
-               *(u16 *) value = readw(virt_addr);
-               break;
-       case 32:
-               *(u32 *) value = readl(virt_addr);
-               break;
-       default:
-               BUG();
-       }
-
-       if (unmap)
-               iounmap(virt_addr);
-       else
-               rcu_read_unlock();
-
-       return AE_OK;
-}
-
 #ifdef readq
 static inline u64 read64(const volatile void __iomem *addr)
 {
@@ -758,7 +728,7 @@ static inline u64 read64(const volatile void __iomem *addr)
 #endif
 
 acpi_status
-acpi_os_read_memory64(acpi_physical_address phys_addr, u64 *value, u32 width)
+acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width)
 {
        void __iomem *virt_addr;
        unsigned int size = width / 8;
@@ -803,45 +773,6 @@ acpi_os_read_memory64(acpi_physical_address phys_addr, u64 *value, u32 width)
        return AE_OK;
 }
 
-acpi_status
-acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width)
-{
-       void __iomem *virt_addr;
-       unsigned int size = width / 8;
-       bool unmap = false;
-
-       rcu_read_lock();
-       virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
-       if (!virt_addr) {
-               rcu_read_unlock();
-               virt_addr = acpi_os_ioremap(phys_addr, size);
-               if (!virt_addr)
-                       return AE_BAD_ADDRESS;
-               unmap = true;
-       }
-
-       switch (width) {
-       case 8:
-               writeb(value, virt_addr);
-               break;
-       case 16:
-               writew(value, virt_addr);
-               break;
-       case 32:
-               writel(value, virt_addr);
-               break;
-       default:
-               BUG();
-       }
-
-       if (unmap)
-               iounmap(virt_addr);
-       else
-               rcu_read_unlock();
-
-       return AE_OK;
-}
-
 #ifdef writeq
 static inline void write64(u64 val, volatile void __iomem *addr)
 {
@@ -856,7 +787,7 @@ static inline void write64(u64 val, volatile void __iomem *addr)
 #endif
 
 acpi_status
-acpi_os_write_memory64(acpi_physical_address phys_addr, u64 value, u32 width)
+acpi_os_write_memory(acpi_physical_address phys_addr, u64 value, u32 width)
 {
        void __iomem *virt_addr;
        unsigned int size = width / 8;
@@ -1641,3 +1572,24 @@ acpi_status acpi_os_terminate(void)
 
        return AE_OK;
 }
+
+acpi_status acpi_os_prepare_sleep(u8 sleep_state, u32 pm1a_control,
+                                 u32 pm1b_control)
+{
+       int rc = 0;
+       if (__acpi_os_prepare_sleep)
+               rc = __acpi_os_prepare_sleep(sleep_state,
+                                            pm1a_control, pm1b_control);
+       if (rc < 0)
+               return AE_ERROR;
+       else if (rc > 0)
+               return AE_CTRL_SKIP;
+
+       return AE_OK;
+}
+
+void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state,
+                              u32 pm1a_ctrl, u32 pm1b_ctrl))
+{
+       __acpi_os_prepare_sleep = func;
+}
index 9ac2a9fa90ff23092c96b9b1b51af5a83f1d598e..7049a7d27c4f8bbd80991be1a633616474c0e2f4 100644 (file)
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 #include "sleep.h"
+#include "internal.h"
 
 #define PREFIX "ACPI: "
 
@@ -77,6 +79,20 @@ static struct acpi_driver acpi_power_driver = {
                },
 };
 
+/*
+ * A power managed device
+ * A device may rely on multiple power resources.
+ * */
+struct acpi_power_managed_device {
+       struct device *dev; /* The physical device */
+       acpi_handle *handle;
+};
+
+struct acpi_power_resource_device {
+       struct acpi_power_managed_device *device;
+       struct acpi_power_resource_device *next;
+};
+
 struct acpi_power_resource {
        struct acpi_device * device;
        acpi_bus_id name;
@@ -84,6 +100,9 @@ struct acpi_power_resource {
        u32 order;
        unsigned int ref_count;
        struct mutex resource_lock;
+
+       /* List of devices relying on this power resource */
+       struct acpi_power_resource_device *devices;
 };
 
 static struct list_head acpi_power_resource_list;
@@ -183,8 +202,26 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
        return 0;
 }
 
+/* Resume the device when all power resources in _PR0 are on */
+static void acpi_power_on_device(struct acpi_power_managed_device *device)
+{
+       struct acpi_device *acpi_dev;
+       acpi_handle handle = device->handle;
+       int state;
+
+       if (acpi_bus_get_device(handle, &acpi_dev))
+               return;
+
+       if(acpi_power_get_inferred_state(acpi_dev, &state))
+               return;
+
+       if (state == ACPI_STATE_D0 && pm_runtime_suspended(device->dev))
+               pm_request_resume(device->dev);
+}
+
 static int __acpi_power_on(struct acpi_power_resource *resource)
 {
+       struct acpi_power_resource_device *device_list = resource->devices;
        acpi_status status = AE_OK;
 
        status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
@@ -197,6 +234,12 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
                          resource->name));
 
+       while (device_list) {
+               acpi_power_on_device(device_list->device);
+
+               device_list = device_list->next;
+       }
+
        return 0;
 }
 
@@ -299,6 +342,125 @@ static int acpi_power_on_list(struct acpi_handle_list *list)
        return result;
 }
 
+static void __acpi_power_resource_unregister_device(struct device *dev,
+               acpi_handle res_handle)
+{
+       struct acpi_power_resource *resource = NULL;
+       struct acpi_power_resource_device *prev, *curr;
+
+       if (acpi_power_get_context(res_handle, &resource))
+               return;
+
+       mutex_lock(&resource->resource_lock);
+       prev = NULL;
+       curr = resource->devices;
+       while (curr) {
+               if (curr->device->dev == dev) {
+                       if (!prev)
+                               resource->devices = curr->next;
+                       else
+                               prev->next = curr->next;
+
+                       kfree(curr);
+                       break;
+               }
+
+               prev = curr;
+               curr = curr->next;
+       }
+       mutex_unlock(&resource->resource_lock);
+}
+
+/* Unlink dev from all power resources in _PR0 */
+void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle)
+{
+       struct acpi_device *acpi_dev;
+       struct acpi_handle_list *list;
+       int i;
+
+       if (!dev || !handle)
+               return;
+
+       if (acpi_bus_get_device(handle, &acpi_dev))
+               return;
+
+       list = &acpi_dev->power.states[ACPI_STATE_D0].resources;
+
+       for (i = 0; i < list->count; i++)
+               __acpi_power_resource_unregister_device(dev,
+                       list->handles[i]);
+}
+
+static int __acpi_power_resource_register_device(
+       struct acpi_power_managed_device *powered_device, acpi_handle handle)
+{
+       struct acpi_power_resource *resource = NULL;
+       struct acpi_power_resource_device *power_resource_device;
+       int result;
+
+       result = acpi_power_get_context(handle, &resource);
+       if (result)
+               return result;
+
+       power_resource_device = kzalloc(
+               sizeof(*power_resource_device), GFP_KERNEL);
+       if (!power_resource_device)
+               return -ENOMEM;
+
+       power_resource_device->device = powered_device;
+
+       mutex_lock(&resource->resource_lock);
+       power_resource_device->next = resource->devices;
+       resource->devices = power_resource_device;
+       mutex_unlock(&resource->resource_lock);
+
+       return 0;
+}
+
+/* Link dev to all power resources in _PR0 */
+int acpi_power_resource_register_device(struct device *dev, acpi_handle handle)
+{
+       struct acpi_device *acpi_dev;
+       struct acpi_handle_list *list;
+       struct acpi_power_managed_device *powered_device;
+       int i, ret;
+
+       if (!dev || !handle)
+               return -ENODEV;
+
+       ret = acpi_bus_get_device(handle, &acpi_dev);
+       if (ret)
+               goto no_power_resource;
+
+       if (!acpi_dev->power.flags.power_resources)
+               goto no_power_resource;
+
+       powered_device = kzalloc(sizeof(*powered_device), GFP_KERNEL);
+       if (!powered_device)
+               return -ENOMEM;
+
+       powered_device->dev = dev;
+       powered_device->handle = handle;
+
+       list = &acpi_dev->power.states[ACPI_STATE_D0].resources;
+
+       for (i = 0; i < list->count; i++) {
+               ret = __acpi_power_resource_register_device(powered_device,
+                       list->handles[i]);
+
+               if (ret) {
+                       acpi_power_resource_unregister_device(dev, handle);
+                       break;
+               }
+       }
+
+       return ret;
+
+no_power_resource:
+       printk(KERN_WARNING PREFIX "Invalid Power Resource to register!");
+       return -ENODEV;
+}
+
 /**
  * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
  *                          ACPI 3.0) _PSW (Power State Wake)
@@ -500,14 +662,14 @@ int acpi_power_transition(struct acpi_device *device, int state)
 {
        int result;
 
-       if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
+       if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
                return -EINVAL;
 
        if (device->power.state == state)
                return 0;
 
        if ((device->power.state < ACPI_STATE_D0)
-           || (device->power.state > ACPI_STATE_D3))
+           || (device->power.state > ACPI_STATE_D3_COLD))
                return -ENODEV;
 
        /* TBD: Resources must be ordered. */
index d4d9cb7e016a0b2d5ea5663f039ae905f77ce92c..0734086537b89732ba805158389b904e94ae7ecf 100644 (file)
@@ -67,6 +67,7 @@
 #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
 #define ACPI_PROCESSOR_NOTIFY_POWER    0x81
 #define ACPI_PROCESSOR_NOTIFY_THROTTLING       0x82
+#define ACPI_PROCESSOR_DEVICE_HID      "ACPI0007"
 
 #define ACPI_PROCESSOR_LIMIT_USER      0
 #define ACPI_PROCESSOR_LIMIT_THERMAL   1
@@ -87,7 +88,7 @@ static int acpi_processor_start(struct acpi_processor *pr);
 
 static const struct acpi_device_id processor_device_ids[] = {
        {ACPI_PROCESSOR_OBJECT_HID, 0},
-       {"ACPI0007", 0},
+       {ACPI_PROCESSOR_DEVICE_HID, 0},
        {"", 0},
 };
 MODULE_DEVICE_TABLE(acpi, processor_device_ids);
@@ -535,8 +536,8 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
                return -ENOMEM;
 
        if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) {
-               kfree(pr);
-               return -ENOMEM;
+               result = -ENOMEM;
+               goto err_free_pr;
        }
 
        pr->handle = device->handle;
@@ -576,7 +577,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
        dev = get_cpu_device(pr->id);
        if (sysfs_create_link(&device->dev.kobj, &dev->kobj, "sysdev")) {
                result = -EFAULT;
-               goto err_free_cpumask;
+               goto err_clear_processor;
        }
 
        /*
@@ -594,9 +595,15 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
 
 err_remove_sysfs:
        sysfs_remove_link(&device->dev.kobj, "sysdev");
+err_clear_processor:
+       /*
+        * processor_device_array is not cleared to allow checks for buggy BIOS
+        */ 
+       per_cpu(processors, pr->id) = NULL;
 err_free_cpumask:
        free_cpumask_var(pr->throttling.shared_cpu_map);
-
+err_free_pr:
+       kfree(pr);
        return result;
 }
 
@@ -741,20 +748,46 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
        return;
 }
 
+static acpi_status is_processor_device(acpi_handle handle)
+{
+       struct acpi_device_info *info;
+       char *hid;
+       acpi_status status;
+
+       status = acpi_get_object_info(handle, &info);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       if (info->type == ACPI_TYPE_PROCESSOR) {
+               kfree(info);
+               return AE_OK;   /* found a processor object */
+       }
+
+       if (!(info->valid & ACPI_VALID_HID)) {
+               kfree(info);
+               return AE_ERROR;
+       }
+
+       hid = info->hardware_id.string;
+       if ((hid == NULL) || strcmp(hid, ACPI_PROCESSOR_DEVICE_HID)) {
+               kfree(info);
+               return AE_ERROR;
+       }
+
+       kfree(info);
+       return AE_OK;   /* found a processor device object */
+}
+
 static acpi_status
 processor_walk_namespace_cb(acpi_handle handle,
                            u32 lvl, void *context, void **rv)
 {
        acpi_status status;
        int *action = context;
-       acpi_object_type type = 0;
 
-       status = acpi_get_type(handle, &type);
+       status = is_processor_device(handle);
        if (ACPI_FAILURE(status))
-               return (AE_OK);
-
-       if (type != ACPI_TYPE_PROCESSOR)
-               return (AE_OK);
+               return AE_OK;   /* not a processor; continue to walk */
 
        switch (*action) {
        case INSTALL_NOTIFY_HANDLER:
@@ -772,7 +805,8 @@ processor_walk_namespace_cb(acpi_handle handle,
                break;
        }
 
-       return (AE_OK);
+       /* found a processor; skip walking underneath */
+       return AE_CTRL_DEPTH;
 }
 
 static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr)
@@ -830,7 +864,7 @@ void acpi_processor_install_hotplug_notify(void)
 {
 #ifdef CONFIG_ACPI_HOTPLUG_CPU
        int action = INSTALL_NOTIFY_HANDLER;
-       acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+       acpi_walk_namespace(ACPI_TYPE_ANY,
                            ACPI_ROOT_OBJECT,
                            ACPI_UINT32_MAX,
                            processor_walk_namespace_cb, NULL, &action, NULL);
@@ -843,7 +877,7 @@ void acpi_processor_uninstall_hotplug_notify(void)
 {
 #ifdef CONFIG_ACPI_HOTPLUG_CPU
        int action = UNINSTALL_NOTIFY_HANDLER;
-       acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+       acpi_walk_namespace(ACPI_TYPE_ANY,
                            ACPI_ROOT_OBJECT,
                            ACPI_UINT32_MAX,
                            processor_walk_namespace_cb, NULL, &action, NULL);
index 0e8e2de2ed3e3a6b18ab07d07713a529295f31aa..b3447f63e46b4f77a7e5edc7dc3bd5d0a7b3d33c 100644 (file)
@@ -770,6 +770,35 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
        return index;
 }
 
+
+/**
+ * acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining)
+ * @dev: the target CPU
+ * @index: the index of suggested state
+ */
+static int acpi_idle_play_dead(struct cpuidle_device *dev, int index)
+{
+       struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+       struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
+
+       ACPI_FLUSH_CPU_CACHE();
+
+       while (1) {
+
+               if (cx->entry_method == ACPI_CSTATE_HALT)
+                       halt();
+               else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) {
+                       inb(cx->address);
+                       /* See comment in acpi_idle_do_entry() */
+                       inl(acpi_gbl_FADT.xpm_timer_block.address);
+               } else
+                       return -ENODEV;
+       }
+
+       /* Never reached */
+       return 0;
+}
+
 /**
  * acpi_idle_enter_simple - enters an ACPI state without BM handling
  * @dev: the target CPU
@@ -1077,12 +1106,14 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
                                state->flags |= CPUIDLE_FLAG_TIME_VALID;
 
                        state->enter = acpi_idle_enter_c1;
+                       state->enter_dead = acpi_idle_play_dead;
                        drv->safe_state_index = count;
                        break;
 
                        case ACPI_STATE_C2:
                        state->flags |= CPUIDLE_FLAG_TIME_VALID;
                        state->enter = acpi_idle_enter_simple;
+                       state->enter_dead = acpi_idle_play_dead;
                        drv->safe_state_index = count;
                        break;
 
@@ -1159,8 +1190,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
         * to make the code that updates C-States be called once.
         */
 
-       if (smp_processor_id() == 0 &&
-                       cpuidle_get_driver() == &acpi_idle_driver) {
+       if (pr->id == 0 && cpuidle_get_driver() == &acpi_idle_driver) {
 
                cpuidle_pause_and_lock();
                /* Protect against cpu-hotplug */
index 3b599abf2b4049ab2cba7794bd654d38fa43a9c7..641b5450a0dbc1f21992864579a19709d595887f 100644 (file)
@@ -57,6 +57,27 @@ ACPI_MODULE_NAME("processor_thermal");
 static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg);
 static unsigned int acpi_thermal_cpufreq_is_init = 0;
 
+#define reduction_pctg(cpu) \
+       per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu))
+
+/*
+ * Emulate "per package data" using per cpu data (which should really be
+ * provided elsewhere)
+ *
+ * Note we can lose a CPU on cpu hotunplug, in this case we forget the state
+ * temporarily. Fortunately that's not a big issue here (I hope)
+ */
+static int phys_package_first_cpu(int cpu)
+{
+       int i;
+       int id = topology_physical_package_id(cpu);
+
+       for_each_online_cpu(i)
+               if (topology_physical_package_id(i) == id)
+                       return i;
+       return 0;
+}
+
 static int cpu_has_cpufreq(unsigned int cpu)
 {
        struct cpufreq_policy policy;
@@ -76,7 +97,7 @@ static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb,
 
        max_freq = (
            policy->cpuinfo.max_freq *
-           (100 - per_cpu(cpufreq_thermal_reduction_pctg, policy->cpu) * 20)
+           (100 - reduction_pctg(policy->cpu) * 20)
        ) / 100;
 
        cpufreq_verify_within_limits(policy, 0, max_freq);
@@ -102,16 +123,28 @@ static int cpufreq_get_cur_state(unsigned int cpu)
        if (!cpu_has_cpufreq(cpu))
                return 0;
 
-       return per_cpu(cpufreq_thermal_reduction_pctg, cpu);
+       return reduction_pctg(cpu);
 }
 
 static int cpufreq_set_cur_state(unsigned int cpu, int state)
 {
+       int i;
+
        if (!cpu_has_cpufreq(cpu))
                return 0;
 
-       per_cpu(cpufreq_thermal_reduction_pctg, cpu) = state;
-       cpufreq_update_policy(cpu);
+       reduction_pctg(cpu) = state;
+
+       /*
+        * Update all the CPUs in the same package because they all
+        * contribute to the temperature and often share the same
+        * frequency.
+        */
+       for_each_online_cpu(i) {
+               if (topology_physical_package_id(i) ==
+                   topology_physical_package_id(cpu))
+                       cpufreq_update_policy(i);
+       }
        return 0;
 }
 
@@ -119,10 +152,6 @@ void acpi_thermal_cpufreq_init(void)
 {
        int i;
 
-       for (i = 0; i < nr_cpu_ids; i++)
-               if (cpu_present(i))
-                       per_cpu(cpufreq_thermal_reduction_pctg, i) = 0;
-
        i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block,
                                      CPUFREQ_POLICY_NOTIFIER);
        if (!i)
index 605a2954ef175e563db7d212d2184c924b7b5013..1d02b7b5ade094dd386cabdfa8a39c959e829ea7 100644 (file)
@@ -769,7 +769,7 @@ static int acpi_read_throttling_status(struct acpi_processor *pr,
                                        u64 *value)
 {
        u32 bit_width, bit_offset;
-       u64 ptc_value;
+       u32 ptc_value;
        u64 ptc_mask;
        struct acpi_processor_throttling *throttling;
        int ret = -1;
@@ -777,12 +777,11 @@ static int acpi_read_throttling_status(struct acpi_processor *pr,
        throttling = &pr->throttling;
        switch (throttling->status_register.space_id) {
        case ACPI_ADR_SPACE_SYSTEM_IO:
-               ptc_value = 0;
                bit_width = throttling->status_register.bit_width;
                bit_offset = throttling->status_register.bit_offset;
 
                acpi_os_read_port((acpi_io_address) throttling->status_register.
-                                 address, (u32 *) &ptc_value,
+                                 address, &ptc_value,
                                  (u32) (bit_width + bit_offset));
                ptc_mask = (1 << bit_width) - 1;
                *value = (u64) ((ptc_value >> bit_offset) & ptc_mask);
index a6c77e8b37bde54c266cae17d620c441a896e3b9..c1d612435939a5d62abb832d1f620faeeac7dc20 100644 (file)
@@ -23,8 +23,7 @@ void acpi_reboot(void)
        /* Is the reset register supported? The spec says we should be
         * checking the bit width and bit offset, but Windows ignores
         * these fields */
-       if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER))
-               return;
+       /* Ignore also acpi_gbl_FADT.flags.ACPI_FADT_RESET_REGISTER */
 
        reset_value = acpi_gbl_FADT.reset_value;
 
index 8ab80bafe3f1604f3e05e7287ed5fe4f9ed1e6fb..767e2dcb96169a9ef676b409b808ff823c280578 100644 (file)
@@ -880,18 +880,22 @@ static int acpi_bus_get_power_flags(struct acpi_device *device)
                        int j;
 
                        device->power.flags.power_resources = 1;
-                       ps->flags.valid = 1;
                        for (j = 0; j < ps->resources.count; j++)
                                acpi_bus_add_power_resource(ps->resources.handles[j]);
                }
 
+               /* The exist of _PR3 indicates D3Cold support */
+               if (i == ACPI_STATE_D3) {
+                       status = acpi_get_handle(device->handle, object_name, &handle);
+                       if (ACPI_SUCCESS(status))
+                               device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
+               }
+
                /* Evaluate "_PSx" to see if we can do explicit sets */
                object_name[2] = 'S';
                status = acpi_get_handle(device->handle, object_name, &handle);
-               if (ACPI_SUCCESS(status)) {
+               if (ACPI_SUCCESS(status))
                        ps->flags.explicit_set = 1;
-                       ps->flags.valid = 1;
-               }
 
                /* State is valid if we have some power control */
                if (ps->resources.count || ps->flags.explicit_set)
index ca191ff978444c2fedf09aa950f8b26d327befbd..1d661b5c3287fb4b5083a6636d13e48643da8d51 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/suspend.h>
 #include <linux/reboot.h>
 #include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
 
 #include <asm/io.h>
 
 #include "internal.h"
 #include "sleep.h"
 
+static unsigned int gts, bfs;
+module_param(gts, uint, 0644);
+module_param(bfs, uint, 0644);
+MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
+MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
+
+static u8 wake_sleep_flags(void)
+{
+       u8 flags = ACPI_NO_OPTIONAL_METHODS;
+
+       if (gts)
+               flags |= ACPI_EXECUTE_GTS;
+       if (bfs)
+               flags |= ACPI_EXECUTE_BFS;
+
+       return flags;
+}
+
 static u8 sleep_states[ACPI_S_STATE_COUNT];
 
 static void acpi_sleep_tts_switch(u32 acpi_state)
@@ -243,6 +263,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
 {
        acpi_status status = AE_OK;
        u32 acpi_state = acpi_target_sleep_state;
+       u8 flags = wake_sleep_flags();
        int error;
 
        ACPI_FLUSH_CPU_CACHE();
@@ -250,7 +271,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
        switch (acpi_state) {
        case ACPI_STATE_S1:
                barrier();
-               status = acpi_enter_sleep_state(acpi_state);
+               status = acpi_enter_sleep_state(acpi_state, flags);
                break;
 
        case ACPI_STATE_S3:
@@ -265,7 +286,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
        acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
 
        /* Reprogram control registers and execute _BFS */
-       acpi_leave_sleep_state_prep(acpi_state);
+       acpi_leave_sleep_state_prep(acpi_state, flags);
 
        /* ACPI 3.0 specs (P62) says that it's the responsibility
         * of the OSPM to clear the status bit [ implying that the
@@ -529,27 +550,30 @@ static int acpi_hibernation_begin(void)
 
 static int acpi_hibernation_enter(void)
 {
+       u8 flags = wake_sleep_flags();
        acpi_status status = AE_OK;
 
        ACPI_FLUSH_CPU_CACHE();
 
        /* This shouldn't return.  If it returns, we have a problem */
-       status = acpi_enter_sleep_state(ACPI_STATE_S4);
+       status = acpi_enter_sleep_state(ACPI_STATE_S4, flags);
        /* Reprogram control registers and execute _BFS */
-       acpi_leave_sleep_state_prep(ACPI_STATE_S4);
+       acpi_leave_sleep_state_prep(ACPI_STATE_S4, flags);
 
        return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
 static void acpi_hibernation_leave(void)
 {
+       u8 flags = wake_sleep_flags();
+
        /*
         * If ACPI is not enabled by the BIOS and the boot kernel, we need to
         * enable it here.
         */
        acpi_enable();
        /* Reprogram control registers and execute _BFS */
-       acpi_leave_sleep_state_prep(ACPI_STATE_S4);
+       acpi_leave_sleep_state_prep(ACPI_STATE_S4, flags);
        /* Check the hardware signature */
        if (facs && s4_hardware_signature != facs->hardware_signature) {
                printk(KERN_EMERG "ACPI: Hardware changed while hibernated, "
@@ -729,6 +753,40 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
 #endif /* CONFIG_PM */
 
 #ifdef CONFIG_PM_SLEEP
+/**
+ * acpi_pm_device_run_wake - Enable/disable wake-up for given device.
+ * @phys_dev: Device to enable/disable the platform to wake-up the system for.
+ * @enable: Whether enable or disable the wake-up functionality.
+ *
+ * Find the ACPI device object corresponding to @pci_dev and try to
+ * enable/disable the GPE associated with it.
+ */
+int acpi_pm_device_run_wake(struct device *phys_dev, bool enable)
+{
+       struct acpi_device *dev;
+       acpi_handle handle;
+
+       if (!device_run_wake(phys_dev))
+               return -EINVAL;
+
+       handle = DEVICE_ACPI_HANDLE(phys_dev);
+       if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) {
+               dev_dbg(phys_dev, "ACPI handle has no context in %s!\n",
+                       __func__);
+               return -ENODEV;
+       }
+
+       if (enable) {
+               acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0);
+               acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
+       } else {
+               acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
+               acpi_disable_wakeup_device_power(dev);
+       }
+
+       return 0;
+}
+
 /**
  *     acpi_pm_device_sleep_wake - enable or disable the system wake-up
  *                                  capability of given device
@@ -770,10 +828,12 @@ static void acpi_power_off_prepare(void)
 
 static void acpi_power_off(void)
 {
+       u8 flags = wake_sleep_flags();
+
        /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */
        printk(KERN_DEBUG "%s called\n", __func__);
        local_irq_disable();
-       acpi_enter_sleep_state(ACPI_STATE_S5);
+       acpi_enter_sleep_state(ACPI_STATE_S5, flags);
 }
 
 /*
@@ -788,13 +848,13 @@ static void __init acpi_gts_bfs_check(void)
 {
        acpi_handle dummy;
 
-       if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_NAME__GTS, &dummy)))
+       if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__GTS, &dummy)))
        {
                printk(KERN_NOTICE PREFIX "BIOS offers _GTS\n");
                printk(KERN_NOTICE PREFIX "If \"acpi.gts=1\" improves suspend, "
                        "please notify linux-acpi@vger.kernel.org\n");
        }
-       if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_NAME__BFS, &dummy)))
+       if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__BFS, &dummy)))
        {
                printk(KERN_NOTICE PREFIX "BIOS offers _BFS\n");
                printk(KERN_NOTICE PREFIX "If \"acpi.bfs=1\" improves resume, "
index 48fbc647b1780965393c623546d208c3c2e63b35..7dbebea1ec31302bcb3eaac60d0aa3dd8fb9f7af 100644 (file)
@@ -941,13 +941,13 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz)
        if (!tz)
                return -EINVAL;
 
-       /* Get temperature [_TMP] (required) */
-       result = acpi_thermal_get_temperature(tz);
+       /* Get trip points [_CRT, _PSV, etc.] (required) */
+       result = acpi_thermal_get_trip_points(tz);
        if (result)
                return result;
 
-       /* Get trip points [_CRT, _PSV, etc.] (required) */
-       result = acpi_thermal_get_trip_points(tz);
+       /* Get temperature [_TMP] (required) */
+       result = acpi_thermal_get_temperature(tz);
        if (result)
                return result;
 
index eaef02afc7cfce7004e3d74e25b733ee4d54cb97..9577b6fa26507cad1db1f2ddf7d6f114abfdf513 100644 (file)
@@ -548,27 +548,27 @@ acpi_video_device_EDID(struct acpi_video_device *device,
  *             1.      The system BIOS should NOT automatically control the brightness 
  *                     level of the LCD when the power changes from AC to DC.
  * Return Value:
- *             -1      wrong arg.
+ *             -EINVAL wrong arg.
  */
 
 static int
 acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
 {
-       u64 status = 0;
+       acpi_status status;
        union acpi_object arg0 = { ACPI_TYPE_INTEGER };
        struct acpi_object_list args = { 1, &arg0 };
 
 
-       if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
-               status = -1;
-               goto Failed;
-       }
+       if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1)
+               return -EINVAL;
        arg0.integer.value = (lcd_flag << 2) | bios_flag;
        video->dos_setting = arg0.integer.value;
-       acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
+       status = acpi_evaluate_object(video->device->handle, "_DOS",
+               &args, NULL);
+       if (ACPI_FAILURE(status))
+               return -EIO;
 
-      Failed:
-       return status;
+       return 0;
 }
 
 /*
@@ -1343,15 +1343,17 @@ static int
 acpi_video_bus_get_devices(struct acpi_video_bus *video,
                           struct acpi_device *device)
 {
-       int status = 0;
+       int status;
        struct acpi_device *dev;
 
-       acpi_video_device_enumerate(video);
+       status = acpi_video_device_enumerate(video);
+       if (status)
+               return status;
 
        list_for_each_entry(dev, &device->children, node) {
 
                status = acpi_video_bus_get_one_device(dev, video);
-               if (ACPI_FAILURE(status)) {
+               if (status) {
                        printk(KERN_WARNING PREFIX
                                        "Can't attach device\n");
                        continue;
@@ -1653,15 +1655,20 @@ static int acpi_video_bus_add(struct acpi_device *device)
        mutex_init(&video->device_list_lock);
        INIT_LIST_HEAD(&video->video_device_list);
 
-       acpi_video_bus_get_devices(video, device);
-       acpi_video_bus_start_devices(video);
+       error = acpi_video_bus_get_devices(video, device);
+       if (error)
+               goto err_free_video;
 
        video->input = input = input_allocate_device();
        if (!input) {
                error = -ENOMEM;
-               goto err_stop_video;
+               goto err_put_video;
        }
 
+       error = acpi_video_bus_start_devices(video);
+       if (error)
+               goto err_free_input_dev;
+
        snprintf(video->phys, sizeof(video->phys),
                "%s/video/input0", acpi_device_hid(video->device));
 
@@ -1682,7 +1689,7 @@ static int acpi_video_bus_add(struct acpi_device *device)
 
        error = input_register_device(input);
        if (error)
-               goto err_free_input_dev;
+               goto err_stop_video;
 
        printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s  rom: %s  post: %s)\n",
               ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
@@ -1692,14 +1699,19 @@ static int acpi_video_bus_add(struct acpi_device *device)
 
        video->pm_nb.notifier_call = acpi_video_resume;
        video->pm_nb.priority = 0;
-       register_pm_notifier(&video->pm_nb);
+       error = register_pm_notifier(&video->pm_nb);
+       if (error)
+               goto err_unregister_input_dev;
 
        return 0;
 
- err_free_input_dev:
-       input_free_device(input);
+ err_unregister_input_dev:
+       input_unregister_device(input);
  err_stop_video:
        acpi_video_bus_stop_devices(video);
+ err_free_input_dev:
+       input_free_device(input);
+ err_put_video:
        acpi_video_bus_put_devices(video);
        kfree(video->attached_array);
  err_free_video:
index 6588f43017bd2803d29e04cd28961d858430828d..87411cebc57725223a994eb6467ad0aa209d3515 100644 (file)
@@ -53,6 +53,52 @@ static void cpuidle_kick_cpus(void) {}
 
 static int __cpuidle_register_device(struct cpuidle_device *dev);
 
+static inline int cpuidle_enter(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int index)
+{
+       struct cpuidle_state *target_state = &drv->states[index];
+       return target_state->enter(dev, drv, index);
+}
+
+static inline int cpuidle_enter_tk(struct cpuidle_device *dev,
+                              struct cpuidle_driver *drv, int index)
+{
+       return cpuidle_wrap_enter(dev, drv, index, cpuidle_enter);
+}
+
+typedef int (*cpuidle_enter_t)(struct cpuidle_device *dev,
+                              struct cpuidle_driver *drv, int index);
+
+static cpuidle_enter_t cpuidle_enter_ops;
+
+/**
+ * cpuidle_play_dead - cpu off-lining
+ *
+ * Only returns in case of an error
+ */
+int cpuidle_play_dead(void)
+{
+       struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
+       struct cpuidle_driver *drv = cpuidle_get_driver();
+       int i, dead_state = -1;
+       int power_usage = -1;
+
+       /* Find lowest-power state that supports long-term idle */
+       for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
+               struct cpuidle_state *s = &drv->states[i];
+
+               if (s->power_usage < power_usage && s->enter_dead) {
+                       power_usage = s->power_usage;
+                       dead_state = i;
+               }
+       }
+
+       if (dead_state != -1)
+               return drv->states[dead_state].enter_dead(dev, dead_state);
+
+       return -ENODEV;
+}
+
 /**
  * cpuidle_idle_call - the main idle loop
  *
@@ -63,7 +109,6 @@ int cpuidle_idle_call(void)
 {
        struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
        struct cpuidle_driver *drv = cpuidle_get_driver();
-       struct cpuidle_state *target_state;
        int next_state, entered_state;
 
        if (off)
@@ -92,12 +137,10 @@ int cpuidle_idle_call(void)
                return 0;
        }
 
-       target_state = &drv->states[next_state];
-
        trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu);
        trace_cpu_idle_rcuidle(next_state, dev->cpu);
 
-       entered_state = target_state->enter(dev, drv, next_state);
+       entered_state = cpuidle_enter_ops(dev, drv, next_state);
 
        trace_power_end_rcuidle(dev->cpu);
        trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
@@ -110,6 +153,8 @@ int cpuidle_idle_call(void)
                dev->states_usage[entered_state].time +=
                                (unsigned long long)dev->last_residency;
                dev->states_usage[entered_state].usage++;
+       } else {
+               dev->last_residency = 0;
        }
 
        /* give the governor an opportunity to reflect on the outcome */
@@ -164,6 +209,37 @@ void cpuidle_resume_and_unlock(void)
 
 EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock);
 
+/**
+ * cpuidle_wrap_enter - performs timekeeping and irqen around enter function
+ * @dev: pointer to a valid cpuidle_device object
+ * @drv: pointer to a valid cpuidle_driver object
+ * @index: index of the target cpuidle state.
+ */
+int cpuidle_wrap_enter(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int index,
+                               int (*enter)(struct cpuidle_device *dev,
+                                       struct cpuidle_driver *drv, int index))
+{
+       ktime_t time_start, time_end;
+       s64 diff;
+
+       time_start = ktime_get();
+
+       index = enter(dev, drv, index);
+
+       time_end = ktime_get();
+
+       local_irq_enable();
+
+       diff = ktime_to_us(ktime_sub(time_end, time_start));
+       if (diff > INT_MAX)
+               diff = INT_MAX;
+
+       dev->last_residency = (int) diff;
+
+       return index;
+}
+
 #ifdef CONFIG_ARCH_HAS_CPU_RELAX
 static int poll_idle(struct cpuidle_device *dev,
                struct cpuidle_driver *drv, int index)
@@ -197,6 +273,7 @@ static void poll_idle_init(struct cpuidle_driver *drv)
        state->power_usage = -1;
        state->flags = 0;
        state->enter = poll_idle;
+       state->disable = 0;
 }
 #else
 static void poll_idle_init(struct cpuidle_driver *drv) {}
@@ -212,13 +289,14 @@ static void poll_idle_init(struct cpuidle_driver *drv) {}
 int cpuidle_enable_device(struct cpuidle_device *dev)
 {
        int ret, i;
+       struct cpuidle_driver *drv = cpuidle_get_driver();
 
        if (dev->enabled)
                return 0;
-       if (!cpuidle_get_driver() || !cpuidle_curr_governor)
+       if (!drv || !cpuidle_curr_governor)
                return -EIO;
        if (!dev->state_count)
-               return -EINVAL;
+               dev->state_count = drv->state_count;
 
        if (dev->registered == 0) {
                ret = __cpuidle_register_device(dev);
@@ -226,13 +304,16 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
                        return ret;
        }
 
-       poll_idle_init(cpuidle_get_driver());
+       cpuidle_enter_ops = drv->en_core_tk_irqen ?
+               cpuidle_enter_tk : cpuidle_enter;
+
+       poll_idle_init(drv);
 
        if ((ret = cpuidle_add_state_sysfs(dev)))
                return ret;
 
        if (cpuidle_curr_governor->enable &&
-           (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev)))
+           (ret = cpuidle_curr_governor->enable(drv, dev)))
                goto fail_sysfs;
 
        for (i = 0; i < dev->state_count; i++) {
index 284d7af5a9c827ab227293fafd52cd27db442864..40cd3f3024df4031f65a118a43e147e8e1b678bf 100644 (file)
@@ -47,7 +47,7 @@ static void __cpuidle_register_driver(struct cpuidle_driver *drv)
  */
 int cpuidle_register_driver(struct cpuidle_driver *drv)
 {
-       if (!drv)
+       if (!drv || !drv->state_count)
                return -EINVAL;
 
        if (cpuidle_disabled())
index ad0952601ae2d2e322f6d5a81e42cc58545cc40e..06335756ea14269bcaaeebc14c0089939a49fff0 100644 (file)
@@ -236,7 +236,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
 {
        struct menu_device *data = &__get_cpu_var(menu_devices);
        int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
-       unsigned int power_usage = -1;
+       int power_usage = -1;
        int i;
        int multiplier;
        struct timespec t;
@@ -280,7 +280,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
         * We want to default to C1 (hlt), not to busy polling
         * unless the timer is happening really really soon.
         */
-       if (data->expected_us > 5)
+       if (data->expected_us > 5 &&
+               drv->states[CPUIDLE_DRIVER_STATE_START].disable == 0)
                data->last_state_idx = CPUIDLE_DRIVER_STATE_START;
 
        /*
@@ -290,6 +291,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
        for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
                struct cpuidle_state *s = &drv->states[i];
 
+               if (s->disable)
+                       continue;
                if (s->target_residency > data->predicted_us)
                        continue;
                if (s->exit_latency > latency_req)
index 3fe41fe4851a62019e61d1fca88edc464236790e..88032b4dc6d271e8b75a7242ef8a127d5d74ec49 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/sysfs.h>
 #include <linux/slab.h>
 #include <linux/cpu.h>
+#include <linux/capability.h>
 
 #include "cpuidle.h"
 
@@ -222,6 +223,9 @@ struct cpuidle_state_attr {
 #define define_one_state_ro(_name, show) \
 static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL)
 
+#define define_one_state_rw(_name, show, store) \
+static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0644, show, store)
+
 #define define_show_state_function(_name) \
 static ssize_t show_state_##_name(struct cpuidle_state *state, \
                         struct cpuidle_state_usage *state_usage, char *buf) \
@@ -229,6 +233,24 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \
        return sprintf(buf, "%u\n", state->_name);\
 }
 
+#define define_store_state_function(_name) \
+static ssize_t store_state_##_name(struct cpuidle_state *state, \
+               const char *buf, size_t size) \
+{ \
+       long value; \
+       int err; \
+       if (!capable(CAP_SYS_ADMIN)) \
+               return -EPERM; \
+       err = kstrtol(buf, 0, &value); \
+       if (err) \
+               return err; \
+       if (value) \
+               state->disable = 1; \
+       else \
+               state->disable = 0; \
+       return size; \
+}
+
 #define define_show_state_ull_function(_name) \
 static ssize_t show_state_##_name(struct cpuidle_state *state, \
                        struct cpuidle_state_usage *state_usage, char *buf) \
@@ -251,6 +273,8 @@ define_show_state_ull_function(usage)
 define_show_state_ull_function(time)
 define_show_state_str_function(name)
 define_show_state_str_function(desc)
+define_show_state_function(disable)
+define_store_state_function(disable)
 
 define_one_state_ro(name, show_state_name);
 define_one_state_ro(desc, show_state_desc);
@@ -258,6 +282,7 @@ define_one_state_ro(latency, show_state_exit_latency);
 define_one_state_ro(power, show_state_power_usage);
 define_one_state_ro(usage, show_state_usage);
 define_one_state_ro(time, show_state_time);
+define_one_state_rw(disable, show_state_disable, store_state_disable);
 
 static struct attribute *cpuidle_state_default_attrs[] = {
        &attr_name.attr,
@@ -266,6 +291,7 @@ static struct attribute *cpuidle_state_default_attrs[] = {
        &attr_power.attr,
        &attr_usage.attr,
        &attr_time.attr,
+       &attr_disable.attr,
        NULL
 };
 
@@ -287,8 +313,22 @@ static ssize_t cpuidle_state_show(struct kobject * kobj,
        return ret;
 }
 
+static ssize_t cpuidle_state_store(struct kobject *kobj,
+       struct attribute *attr, const char *buf, size_t size)
+{
+       int ret = -EIO;
+       struct cpuidle_state *state = kobj_to_state(kobj);
+       struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);
+
+       if (cattr->store)
+               ret = cattr->store(state, buf, size);
+
+       return ret;
+}
+
 static const struct sysfs_ops cpuidle_state_sysfs_ops = {
        .show = cpuidle_state_show,
+       .store = cpuidle_state_store,
 };
 
 static void cpuidle_state_sysfs_release(struct kobject *kobj)
index 65334c49b71e92bb3c73c01306a98c86eb90b732..c81ef7e10e08283ce8eaf4fadf6166b5d3ddc19c 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/dmaengine.h>
 #include <linux/delay.h>
+#include <linux/fsl/mxs-dma.h>
 
 #include <asm/irq.h>
 #include <mach/mxs.h>
-#include <mach/dma.h>
 #include <mach/common.h>
 
 #include "dmaengine.h"
@@ -337,10 +337,32 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan)
        clk_disable_unprepare(mxs_dma->clk);
 }
 
+/*
+ * How to use the flags for ->device_prep_slave_sg() :
+ *    [1] If there is only one DMA command in the DMA chain, the code should be:
+ *            ......
+ *            ->device_prep_slave_sg(DMA_CTRL_ACK);
+ *            ......
+ *    [2] If there are two DMA commands in the DMA chain, the code should be
+ *            ......
+ *            ->device_prep_slave_sg(0);
+ *            ......
+ *            ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ *            ......
+ *    [3] If there are more than two DMA commands in the DMA chain, the code
+ *        should be:
+ *            ......
+ *            ->device_prep_slave_sg(0);                                // First
+ *            ......
+ *            ->device_prep_slave_sg(DMA_PREP_INTERRUPT [| DMA_CTRL_ACK]);
+ *            ......
+ *            ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK); // Last
+ *            ......
+ */
 static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
                struct dma_chan *chan, struct scatterlist *sgl,
                unsigned int sg_len, enum dma_transfer_direction direction,
-               unsigned long append, void *context)
+               unsigned long flags, void *context)
 {
        struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
        struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
@@ -348,6 +370,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
        struct scatterlist *sg;
        int i, j;
        u32 *pio;
+       bool append = flags & DMA_PREP_INTERRUPT;
        int idx = append ? mxs_chan->desc_count : 0;
 
        if (mxs_chan->status == DMA_IN_PROGRESS && !append)
@@ -374,7 +397,6 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
                ccw->bits |= CCW_CHAIN;
                ccw->bits &= ~CCW_IRQ;
                ccw->bits &= ~CCW_DEC_SEM;
-               ccw->bits &= ~CCW_WAIT4END;
        } else {
                idx = 0;
        }
@@ -389,7 +411,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
                ccw->bits = 0;
                ccw->bits |= CCW_IRQ;
                ccw->bits |= CCW_DEC_SEM;
-               ccw->bits |= CCW_WAIT4END;
+               if (flags & DMA_CTRL_ACK)
+                       ccw->bits |= CCW_WAIT4END;
                ccw->bits |= CCW_HALT_ON_TERM;
                ccw->bits |= CCW_TERM_FLUSH;
                ccw->bits |= BF_CCW(sg_len, PIO_NUM);
@@ -420,7 +443,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
                                ccw->bits &= ~CCW_CHAIN;
                                ccw->bits |= CCW_IRQ;
                                ccw->bits |= CCW_DEC_SEM;
-                               ccw->bits |= CCW_WAIT4END;
+                               if (flags & DMA_CTRL_ACK)
+                                       ccw->bits |= CCW_WAIT4END;
                        }
                }
        }
index 24044dacbf70086a90c6191dc9e310a0608b84ca..c65b5fa69f1eb9b5d7fa75356d2dd9d91abc8cb4 100644 (file)
@@ -107,6 +107,9 @@ static int __init amijoy_init(void)
        int i, j;
        int err;
 
+       if (!MACH_IS_AMIGA)
+               return -ENODEV;
+
        for (i = 0; i < 2; i++) {
                if (!amijoy[i])
                        continue;
index ed1ed469d0850028118683c4bf66df11ef03effc..62bfce468f9f0a5dd8c5aa79fd1e6872923ffe0d 100644 (file)
 #include <linux/gpio.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
+#include <linux/spinlock.h>
 
 struct gpio_button_data {
-       struct gpio_keys_button *button;
+       const struct gpio_keys_button *button;
        struct input_dev *input;
        struct timer_list timer;
        struct work_struct work;
-       int timer_debounce;     /* in msecs */
+       unsigned int timer_debounce;    /* in msecs */
+       unsigned int irq;
+       spinlock_t lock;
        bool disabled;
+       bool key_pressed;
 };
 
 struct gpio_keys_drvdata {
@@ -114,7 +118,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
                /*
                 * Disable IRQ and possible debouncing timer.
                 */
-               disable_irq(gpio_to_irq(bdata->button->gpio));
+               disable_irq(bdata->irq);
                if (bdata->timer_debounce)
                        del_timer_sync(&bdata->timer);
 
@@ -135,7 +139,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
 static void gpio_keys_enable_button(struct gpio_button_data *bdata)
 {
        if (bdata->disabled) {
-               enable_irq(gpio_to_irq(bdata->button->gpio));
+               enable_irq(bdata->irq);
                bdata->disabled = false;
        }
 }
@@ -195,7 +199,7 @@ static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata,
  * @type: button type (%EV_KEY, %EV_SW)
  *
  * This function parses stringified bitmap from @buf and disables/enables
- * GPIO buttons accordinly. Returns 0 on success and negative error
+ * GPIO buttons accordingly. Returns 0 on success and negative error
  * on failure.
  */
 static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
@@ -320,9 +324,9 @@ static struct attribute_group gpio_keys_attr_group = {
        .attrs = gpio_keys_attrs,
 };
 
-static void gpio_keys_report_event(struct gpio_button_data *bdata)
+static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
 {
-       struct gpio_keys_button *button = bdata->button;
+       const struct gpio_keys_button *button = bdata->button;
        struct input_dev *input = bdata->input;
        unsigned int type = button->type ?: EV_KEY;
        int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
@@ -336,27 +340,26 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
        input_sync(input);
 }
 
-static void gpio_keys_work_func(struct work_struct *work)
+static void gpio_keys_gpio_work_func(struct work_struct *work)
 {
        struct gpio_button_data *bdata =
                container_of(work, struct gpio_button_data, work);
 
-       gpio_keys_report_event(bdata);
+       gpio_keys_gpio_report_event(bdata);
 }
 
-static void gpio_keys_timer(unsigned long _data)
+static void gpio_keys_gpio_timer(unsigned long _data)
 {
-       struct gpio_button_data *data = (struct gpio_button_data *)_data;
+       struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
 
-       schedule_work(&data->work);
+       schedule_work(&bdata->work);
 }
 
-static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
+static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
 {
        struct gpio_button_data *bdata = dev_id;
-       struct gpio_keys_button *button = bdata->button;
 
-       BUG_ON(irq != gpio_to_irq(button->gpio));
+       BUG_ON(irq != bdata->irq);
 
        if (bdata->timer_debounce)
                mod_timer(&bdata->timer,
@@ -367,50 +370,133 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static void gpio_keys_irq_timer(unsigned long _data)
+{
+       struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
+       struct input_dev *input = bdata->input;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bdata->lock, flags);
+       if (bdata->key_pressed) {
+               input_event(input, EV_KEY, bdata->button->code, 0);
+               input_sync(input);
+               bdata->key_pressed = false;
+       }
+       spin_unlock_irqrestore(&bdata->lock, flags);
+}
+
+static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
+{
+       struct gpio_button_data *bdata = dev_id;
+       const struct gpio_keys_button *button = bdata->button;
+       struct input_dev *input = bdata->input;
+       unsigned long flags;
+
+       BUG_ON(irq != bdata->irq);
+
+       spin_lock_irqsave(&bdata->lock, flags);
+
+       if (!bdata->key_pressed) {
+               input_event(input, EV_KEY, button->code, 1);
+               input_sync(input);
+
+               if (!bdata->timer_debounce) {
+                       input_event(input, EV_KEY, button->code, 0);
+                       input_sync(input);
+                       goto out;
+               }
+
+               bdata->key_pressed = true;
+       }
+
+       if (bdata->timer_debounce)
+               mod_timer(&bdata->timer,
+                       jiffies + msecs_to_jiffies(bdata->timer_debounce));
+out:
+       spin_unlock_irqrestore(&bdata->lock, flags);
+       return IRQ_HANDLED;
+}
+
 static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
+                                        struct input_dev *input,
                                         struct gpio_button_data *bdata,
-                                        struct gpio_keys_button *button)
+                                        const struct gpio_keys_button *button)
 {
        const char *desc = button->desc ? button->desc : "gpio_keys";
        struct device *dev = &pdev->dev;
+       irq_handler_t isr;
        unsigned long irqflags;
        int irq, error;
 
-       setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
-       INIT_WORK(&bdata->work, gpio_keys_work_func);
+       bdata->input = input;
+       bdata->button = button;
+       spin_lock_init(&bdata->lock);
 
-       error = gpio_request(button->gpio, desc);
-       if (error < 0) {
-               dev_err(dev, "failed to request GPIO %d, error %d\n",
-                       button->gpio, error);
-               goto fail2;
-       }
+       if (gpio_is_valid(button->gpio)) {
 
-       error = gpio_direction_input(button->gpio);
-       if (error < 0) {
-               dev_err(dev, "failed to configure"
-                       " direction for GPIO %d, error %d\n",
-                       button->gpio, error);
-               goto fail3;
-       }
+               error = gpio_request(button->gpio, desc);
+               if (error < 0) {
+                       dev_err(dev, "Failed to request GPIO %d, error %d\n",
+                               button->gpio, error);
+                       return error;
+               }
 
-       if (button->debounce_interval) {
-               error = gpio_set_debounce(button->gpio,
-                                         button->debounce_interval * 1000);
-               /* use timer if gpiolib doesn't provide debounce */
-               if (error < 0)
-                       bdata->timer_debounce = button->debounce_interval;
-       }
+               error = gpio_direction_input(button->gpio);
+               if (error < 0) {
+                       dev_err(dev,
+                               "Failed to configure direction for GPIO %d, error %d\n",
+                               button->gpio, error);
+                       goto fail;
+               }
 
-       irq = gpio_to_irq(button->gpio);
-       if (irq < 0) {
-               error = irq;
-               dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
-                       button->gpio, error);
-               goto fail3;
+               if (button->debounce_interval) {
+                       error = gpio_set_debounce(button->gpio,
+                                       button->debounce_interval * 1000);
+                       /* use timer if gpiolib doesn't provide debounce */
+                       if (error < 0)
+                               bdata->timer_debounce =
+                                               button->debounce_interval;
+               }
+
+               irq = gpio_to_irq(button->gpio);
+               if (irq < 0) {
+                       error = irq;
+                       dev_err(dev,
+                               "Unable to get irq number for GPIO %d, error %d\n",
+                               button->gpio, error);
+                       goto fail;
+               }
+               bdata->irq = irq;
+
+               INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);
+               setup_timer(&bdata->timer,
+                           gpio_keys_gpio_timer, (unsigned long)bdata);
+
+               isr = gpio_keys_gpio_isr;
+               irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+       } else {
+               if (!button->irq) {
+                       dev_err(dev, "No IRQ specified\n");
+                       return -EINVAL;
+               }
+               bdata->irq = button->irq;
+
+               if (button->type && button->type != EV_KEY) {
+                       dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");
+                       return -EINVAL;
+               }
+
+               bdata->timer_debounce = button->debounce_interval;
+               setup_timer(&bdata->timer,
+                           gpio_keys_irq_timer, (unsigned long)bdata);
+
+               isr = gpio_keys_irq_isr;
+               irqflags = 0;
        }
 
-       irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+       input_set_capability(input, button->type ?: EV_KEY, button->code);
+
        /*
         * If platform has specified that the button can be disabled,
         * we don't want it to share the interrupt line.
@@ -418,18 +504,19 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
        if (!button->can_disable)
                irqflags |= IRQF_SHARED;
 
-       error = request_threaded_irq(irq, NULL, gpio_keys_isr, irqflags, desc, bdata);
+       error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata);
        if (error < 0) {
                dev_err(dev, "Unable to claim irq %d; error %d\n",
-                       irq, error);
-               goto fail3;
+                       bdata->irq, error);
+               goto fail;
        }
 
        return 0;
 
-fail3:
-       gpio_free(button->gpio);
-fail2:
+fail:
+       if (gpio_is_valid(button->gpio))
+               gpio_free(button->gpio);
+
        return error;
 }
 
@@ -547,9 +634,19 @@ static int gpio_keys_get_devtree_pdata(struct device *dev,
 
 #endif
 
+static void gpio_remove_key(struct gpio_button_data *bdata)
+{
+       free_irq(bdata->irq, bdata);
+       if (bdata->timer_debounce)
+               del_timer_sync(&bdata->timer);
+       cancel_work_sync(&bdata->work);
+       if (gpio_is_valid(bdata->button->gpio))
+               gpio_free(bdata->button->gpio);
+}
+
 static int __devinit gpio_keys_probe(struct platform_device *pdev)
 {
-       struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+       const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
        struct gpio_keys_drvdata *ddata;
        struct device *dev = &pdev->dev;
        struct gpio_keys_platform_data alt_pdata;
@@ -599,21 +696,15 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
                __set_bit(EV_REP, input->evbit);
 
        for (i = 0; i < pdata->nbuttons; i++) {
-               struct gpio_keys_button *button = &pdata->buttons[i];
+               const struct gpio_keys_button *button = &pdata->buttons[i];
                struct gpio_button_data *bdata = &ddata->data[i];
-               unsigned int type = button->type ?: EV_KEY;
-
-               bdata->input = input;
-               bdata->button = button;
 
-               error = gpio_keys_setup_key(pdev, bdata, button);
+               error = gpio_keys_setup_key(pdev, input, bdata, button);
                if (error)
                        goto fail2;
 
                if (button->wakeup)
                        wakeup = 1;
-
-               input_set_capability(input, type, button->code);
        }
 
        error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
@@ -630,9 +721,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
                goto fail3;
        }
 
-       /* get current state of buttons */
-       for (i = 0; i < pdata->nbuttons; i++)
-               gpio_keys_report_event(&ddata->data[i]);
+       /* get current state of buttons that are connected to GPIOs */
+       for (i = 0; i < pdata->nbuttons; i++) {
+               struct gpio_button_data *bdata = &ddata->data[i];
+               if (gpio_is_valid(bdata->button->gpio))
+                       gpio_keys_gpio_report_event(bdata);
+       }
        input_sync(input);
 
        device_init_wakeup(&pdev->dev, wakeup);
@@ -642,13 +736,8 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
  fail3:
        sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
  fail2:
-       while (--i >= 0) {
-               free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
-               if (ddata->data[i].timer_debounce)
-                       del_timer_sync(&ddata->data[i].timer);
-               cancel_work_sync(&ddata->data[i].work);
-               gpio_free(pdata->buttons[i].gpio);
-       }
+       while (--i >= 0)
+               gpio_remove_key(&ddata->data[i]);
 
        platform_set_drvdata(pdev, NULL);
  fail1:
@@ -671,14 +760,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
 
        device_init_wakeup(&pdev->dev, 0);
 
-       for (i = 0; i < ddata->n_buttons; i++) {
-               int irq = gpio_to_irq(ddata->data[i].button->gpio);
-               free_irq(irq, &ddata->data[i]);
-               if (ddata->data[i].timer_debounce)
-                       del_timer_sync(&ddata->data[i].timer);
-               cancel_work_sync(&ddata->data[i].work);
-               gpio_free(ddata->data[i].button->gpio);
-       }
+       for (i = 0; i < ddata->n_buttons; i++)
+               gpio_remove_key(&ddata->data[i]);
 
        input_unregister_device(input);
 
@@ -703,11 +786,9 @@ static int gpio_keys_suspend(struct device *dev)
 
        if (device_may_wakeup(dev)) {
                for (i = 0; i < ddata->n_buttons; i++) {
-                       struct gpio_keys_button *button = ddata->data[i].button;
-                       if (button->wakeup) {
-                               int irq = gpio_to_irq(button->gpio);
-                               enable_irq_wake(irq);
-                       }
+                       struct gpio_button_data *bdata = &ddata->data[i];
+                       if (bdata->button->wakeup)
+                               enable_irq_wake(bdata->irq);
                }
        }
 
@@ -720,14 +801,12 @@ static int gpio_keys_resume(struct device *dev)
        int i;
 
        for (i = 0; i < ddata->n_buttons; i++) {
+               struct gpio_button_data *bdata = &ddata->data[i];
+               if (bdata->button->wakeup && device_may_wakeup(dev))
+                       disable_irq_wake(bdata->irq);
 
-               struct gpio_keys_button *button = ddata->data[i].button;
-               if (button->wakeup && device_may_wakeup(dev)) {
-                       int irq = gpio_to_irq(button->gpio);
-                       disable_irq_wake(irq);
-               }
-
-               gpio_keys_report_event(&ddata->data[i]);
+               if (gpio_is_valid(bdata->button->gpio))
+                       gpio_keys_gpio_report_event(bdata);
        }
        input_sync(ddata->input);
 
index 21c42f852343e4fbfe2905d9dc5b75b96b9670db..fe4ac95ca6c8d340b23ac20909c8407c14637c98 100644 (file)
@@ -630,6 +630,7 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev)
        if (!np)
                return NULL;
 
+       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
                return NULL;
 
index 2a77a52d2e62a53e53f71c4a46c9db3726c1f484..a977bfaa6821faef1c2993c919f77a854b9a03d8 100644 (file)
@@ -2,7 +2,7 @@
  * Finger Sensing Pad PS/2 mouse driver.
  *
  * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
- * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation.
+ * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
  *
  *   This program is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU General Public License
@@ -21,6 +21,7 @@
 
 #include <linux/module.h>
 #include <linux/input.h>
+#include <linux/input/mt.h>
 #include <linux/ctype.h>
 #include <linux/libps2.h>
 #include <linux/serio.h>
@@ -36,6 +37,9 @@
 #define        FSP_CMD_TIMEOUT         200
 #define        FSP_CMD_TIMEOUT2        30
 
+#define        GET_ABS_X(packet)       ((packet[1] << 2) | ((packet[3] >> 2) & 0x03))
+#define        GET_ABS_Y(packet)       ((packet[2] << 2) | (packet[3] & 0x03))
+
 /** Driver version. */
 static const char fsp_drv_ver[] = "1.0.0-K";
 
@@ -128,8 +132,9 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
  out:
        ps2_end_command(ps2dev);
        psmouse_activate(psmouse);
-       dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
-               reg_addr, *reg_val, rc);
+       psmouse_dbg(psmouse,
+                   "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
+                   reg_addr, *reg_val, rc);
        return rc;
 }
 
@@ -179,8 +184,9 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
 
  out:
        ps2_end_command(ps2dev);
-       dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
-               reg_addr, reg_val, rc);
+       psmouse_dbg(psmouse,
+                   "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
+                   reg_addr, reg_val, rc);
        return rc;
 }
 
@@ -237,8 +243,9 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
  out:
        ps2_end_command(ps2dev);
        psmouse_activate(psmouse);
-       dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n",
-               *reg_val, rc);
+       psmouse_dbg(psmouse,
+                   "READ PAGE REG: 0x%02x (rc = %d)\n",
+                   *reg_val, rc);
        return rc;
 }
 
@@ -274,8 +281,9 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
 
  out:
        ps2_end_command(ps2dev);
-       dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
-               reg_val, rc);
+       psmouse_dbg(psmouse,
+                   "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
+                   reg_val, rc);
        return rc;
 }
 
@@ -319,7 +327,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
        int res = 0;
 
        if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) {
-               dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n");
+               psmouse_err(psmouse, "Unable get OPC state.\n");
                return -EIO;
        }
 
@@ -336,8 +344,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
        }
 
        if (res != 0) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Unable to enable OPC tag.\n");
+               psmouse_err(psmouse, "Unable to enable OPC tag.\n");
                res = -EIO;
        }
 
@@ -615,18 +622,40 @@ static struct attribute_group fsp_attribute_group = {
        .attrs = fsp_attributes,
 };
 
-#ifdef FSP_DEBUG
-static void fsp_packet_debug(unsigned char packet[])
+#ifdef FSP_DEBUG
+static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
 {
        static unsigned int ps2_packet_cnt;
        static unsigned int ps2_last_second;
        unsigned int jiffies_msec;
+       const char *packet_type = "UNKNOWN";
+       unsigned short abs_x = 0, abs_y = 0;
+
+       /* Interpret & dump the packet data. */
+       switch (packet[0] >> FSP_PKT_TYPE_SHIFT) {
+       case FSP_PKT_TYPE_ABS:
+               packet_type = "Absolute";
+               abs_x = GET_ABS_X(packet);
+               abs_y = GET_ABS_Y(packet);
+               break;
+       case FSP_PKT_TYPE_NORMAL:
+               packet_type = "Normal";
+               break;
+       case FSP_PKT_TYPE_NOTIFY:
+               packet_type = "Notify";
+               break;
+       case FSP_PKT_TYPE_NORMAL_OPC:
+               packet_type = "Normal-OPC";
+               break;
+       }
 
        ps2_packet_cnt++;
        jiffies_msec = jiffies_to_msecs(jiffies);
        psmouse_dbg(psmouse,
-                   "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n",
-                   jiffies_msec, packet[0], packet[1], packet[2], packet[3]);
+                   "%08dms %s packets: %02x, %02x, %02x, %02x; "
+                   "abs_x: %d, abs_y: %d\n",
+                   jiffies_msec, packet_type,
+                   packet[0], packet[1], packet[2], packet[3], abs_x, abs_y);
 
        if (jiffies_msec - ps2_last_second > 1000) {
                psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt);
@@ -635,17 +664,29 @@ static void fsp_packet_debug(unsigned char packet[])
        }
 }
 #else
-static void fsp_packet_debug(unsigned char packet[])
+static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
 {
 }
 #endif
 
+static void fsp_set_slot(struct input_dev *dev, int slot, bool active,
+                        unsigned int x, unsigned int y)
+{
+       input_mt_slot(dev, slot);
+       input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+       if (active) {
+               input_report_abs(dev, ABS_MT_POSITION_X, x);
+               input_report_abs(dev, ABS_MT_POSITION_Y, y);
+       }
+}
+
 static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
 {
        struct input_dev *dev = psmouse->dev;
        struct fsp_data *ad = psmouse->private;
        unsigned char *packet = psmouse->packet;
        unsigned char button_status = 0, lscroll = 0, rscroll = 0;
+       unsigned short abs_x, abs_y, fgrs = 0;
        int rel_x, rel_y;
 
        if (psmouse->pktcnt < 4)
@@ -655,16 +696,76 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
         * Full packet accumulated, process it
         */
 
+       fsp_packet_debug(psmouse, packet);
+
        switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
        case FSP_PKT_TYPE_ABS:
-               dev_warn(&psmouse->ps2dev.serio->dev,
-                        "Unexpected absolute mode packet, ignored.\n");
+               abs_x = GET_ABS_X(packet);
+               abs_y = GET_ABS_Y(packet);
+
+               if (packet[0] & FSP_PB0_MFMC) {
+                       /*
+                        * MFMC packet: assume that there are two fingers on
+                        * pad
+                        */
+                       fgrs = 2;
+
+                       /* MFMC packet */
+                       if (packet[0] & FSP_PB0_MFMC_FGR2) {
+                               /* 2nd finger */
+                               if (ad->last_mt_fgr == 2) {
+                                       /*
+                                        * workaround for buggy firmware
+                                        * which doesn't clear MFMC bit if
+                                        * the 1st finger is up
+                                        */
+                                       fgrs = 1;
+                                       fsp_set_slot(dev, 0, false, 0, 0);
+                               }
+                               ad->last_mt_fgr = 2;
+
+                               fsp_set_slot(dev, 1, fgrs == 2, abs_x, abs_y);
+                       } else {
+                               /* 1st finger */
+                               if (ad->last_mt_fgr == 1) {
+                                       /*
+                                        * workaround for buggy firmware
+                                        * which doesn't clear MFMC bit if
+                                        * the 2nd finger is up
+                                        */
+                                       fgrs = 1;
+                                       fsp_set_slot(dev, 1, false, 0, 0);
+                               }
+                               ad->last_mt_fgr = 1;
+                               fsp_set_slot(dev, 0, fgrs != 0, abs_x, abs_y);
+                       }
+               } else {
+                       /* SFAC packet */
+
+                       /* no multi-finger information */
+                       ad->last_mt_fgr = 0;
+
+                       if (abs_x != 0 && abs_y != 0)
+                               fgrs = 1;
+
+                       fsp_set_slot(dev, 0, fgrs > 0, abs_x, abs_y);
+                       fsp_set_slot(dev, 1, false, 0, 0);
+               }
+               if (fgrs > 0) {
+                       input_report_abs(dev, ABS_X, abs_x);
+                       input_report_abs(dev, ABS_Y, abs_y);
+               }
+               input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+               input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+               input_report_key(dev, BTN_TOUCH, fgrs);
+               input_report_key(dev, BTN_TOOL_FINGER, fgrs == 1);
+               input_report_key(dev, BTN_TOOL_DOUBLETAP, fgrs == 2);
                break;
 
        case FSP_PKT_TYPE_NORMAL_OPC:
                /* on-pad click, filter it if necessary */
                if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
-                       packet[0] &= ~BIT(0);
+                       packet[0] &= ~FSP_PB0_LBTN;
                /* fall through */
 
        case FSP_PKT_TYPE_NORMAL:
@@ -711,8 +812,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
 
        input_sync(dev);
 
-       fsp_packet_debug(packet);
-
        return PSMOUSE_FULL_PACKET;
 }
 
@@ -736,42 +835,106 @@ static int fsp_activate_protocol(struct psmouse *psmouse)
 
        ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
        if (param[0] != 0x04) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Unable to enable 4 bytes packet format.\n");
+               psmouse_err(psmouse,
+                           "Unable to enable 4 bytes packet format.\n");
                return -EIO;
        }
 
-       if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Unable to read SYSCTL5 register.\n");
-               return -EIO;
-       }
+       if (pad->ver < FSP_VER_STL3888_C0) {
+               /* Preparing relative coordinates output for older hardware */
+               if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
+                       psmouse_err(psmouse,
+                                   "Unable to read SYSCTL5 register.\n");
+                       return -EIO;
+               }
 
-       val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
-       /* Ensure we are not in absolute mode */
-       val &= ~FSP_BIT_EN_PKT_G0;
-       if (pad->buttons == 0x06) {
-               /* Left/Middle/Right & Scroll Up/Down/Right/Left */
-               val |= FSP_BIT_EN_MSID6;
-       }
+               if (fsp_get_buttons(psmouse, &pad->buttons)) {
+                       psmouse_err(psmouse,
+                                   "Unable to retrieve number of buttons.\n");
+                       return -EIO;
+               }
 
-       if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Unable to set up required mode bits.\n");
-               return -EIO;
+               val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
+               /* Ensure we are not in absolute mode */
+               val &= ~FSP_BIT_EN_PKT_G0;
+               if (pad->buttons == 0x06) {
+                       /* Left/Middle/Right & Scroll Up/Down/Right/Left */
+                       val |= FSP_BIT_EN_MSID6;
+               }
+
+               if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
+                       psmouse_err(psmouse,
+                                   "Unable to set up required mode bits.\n");
+                       return -EIO;
+               }
+
+               /*
+                * Enable OPC tags such that driver can tell the difference
+                * between on-pad and real button click
+                */
+               if (fsp_opc_tag_enable(psmouse, true))
+                       psmouse_warn(psmouse,
+                                    "Failed to enable OPC tag mode.\n");
+               /* enable on-pad click by default */
+               pad->flags |= FSPDRV_FLAG_EN_OPC;
+
+               /* Enable on-pad vertical and horizontal scrolling */
+               fsp_onpad_vscr(psmouse, true);
+               fsp_onpad_hscr(psmouse, true);
+       } else {
+               /* Enable absolute coordinates output for Cx/Dx hardware */
+               if (fsp_reg_write(psmouse, FSP_REG_SWC1,
+                                 FSP_BIT_SWC1_EN_ABS_1F |
+                                 FSP_BIT_SWC1_EN_ABS_2F |
+                                 FSP_BIT_SWC1_EN_FUP_OUT |
+                                 FSP_BIT_SWC1_EN_ABS_CON)) {
+                       psmouse_err(psmouse,
+                                   "Unable to enable absolute coordinates output.\n");
+                       return -EIO;
+               }
        }
 
-       /*
-        * Enable OPC tags such that driver can tell the difference between
-        * on-pad and real button click
-        */
-       if (fsp_opc_tag_enable(psmouse, true))
-               dev_warn(&psmouse->ps2dev.serio->dev,
-                        "Failed to enable OPC tag mode.\n");
+       return 0;
+}
 
-       /* Enable on-pad vertical and horizontal scrolling */
-       fsp_onpad_vscr(psmouse, true);
-       fsp_onpad_hscr(psmouse, true);
+static int fsp_set_input_params(struct psmouse *psmouse)
+{
+       struct input_dev *dev = psmouse->dev;
+       struct fsp_data *pad = psmouse->private;
+
+       if (pad->ver < FSP_VER_STL3888_C0) {
+               __set_bit(BTN_MIDDLE, dev->keybit);
+               __set_bit(BTN_BACK, dev->keybit);
+               __set_bit(BTN_FORWARD, dev->keybit);
+               __set_bit(REL_WHEEL, dev->relbit);
+               __set_bit(REL_HWHEEL, dev->relbit);
+       } else {
+               /*
+                * Hardware prior to Cx performs much better in relative mode;
+                * hence, only enable absolute coordinates output as well as
+                * multi-touch output for the newer hardware.
+                *
+                * Maximum coordinates can be computed as:
+                *
+                *      number of scanlines * 64 - 57
+                *
+                * where number of X/Y scanline lines are 16/12.
+                */
+               int abs_x = 967, abs_y = 711;
+
+               __set_bit(EV_ABS, dev->evbit);
+               __clear_bit(EV_REL, dev->evbit);
+               __set_bit(BTN_TOUCH, dev->keybit);
+               __set_bit(BTN_TOOL_FINGER, dev->keybit);
+               __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+               __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+
+               input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
+               input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
+               input_mt_init_slots(dev, 2);
+               input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
+               input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
+       }
 
        return 0;
 }
@@ -829,18 +992,16 @@ static int fsp_reconnect(struct psmouse *psmouse)
 int fsp_init(struct psmouse *psmouse)
 {
        struct fsp_data *priv;
-       int ver, rev, buttons;
+       int ver, rev;
        int error;
 
        if (fsp_get_version(psmouse, &ver) ||
-           fsp_get_revision(psmouse, &rev) ||
-           fsp_get_buttons(psmouse, &buttons)) {
+           fsp_get_revision(psmouse, &rev)) {
                return -ENODEV;
        }
 
-       psmouse_info(psmouse,
-                    "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n",
-                    ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7);
+       psmouse_info(psmouse, "Finger Sensing Pad, hw: %d.%d.%d, sw: %s\n",
+                    ver >> 4, ver & 0x0F, rev, fsp_drv_ver);
 
        psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);
        if (!priv)
@@ -848,17 +1009,6 @@ int fsp_init(struct psmouse *psmouse)
 
        priv->ver = ver;
        priv->rev = rev;
-       priv->buttons = buttons;
-
-       /* enable on-pad click by default */
-       priv->flags |= FSPDRV_FLAG_EN_OPC;
-
-       /* Set up various supported input event bits */
-       __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
-       __set_bit(BTN_BACK, psmouse->dev->keybit);
-       __set_bit(BTN_FORWARD, psmouse->dev->keybit);
-       __set_bit(REL_WHEEL, psmouse->dev->relbit);
-       __set_bit(REL_HWHEEL, psmouse->dev->relbit);
 
        psmouse->protocol_handler = fsp_process_byte;
        psmouse->disconnect = fsp_disconnect;
@@ -866,16 +1016,20 @@ int fsp_init(struct psmouse *psmouse)
        psmouse->cleanup = fsp_reset;
        psmouse->pktsize = 4;
 
-       /* set default packet output based on number of buttons we found */
        error = fsp_activate_protocol(psmouse);
        if (error)
                goto err_out;
 
+       /* Set up various supported input event bits */
+       error = fsp_set_input_params(psmouse);
+       if (error)
+               goto err_out;
+
        error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
                                   &fsp_attribute_group);
        if (error) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Failed to create sysfs attributes (%d)", error);
+               psmouse_err(psmouse,
+                           "Failed to create sysfs attributes (%d)", error);
                goto err_out;
        }
 
index 2e4af24f8c1586b6ecfb3c7f8325cbeb34eb4ae5..334de19e5ddb9c4ac0387f3e4fe3f90cb612cbcf 100644 (file)
@@ -2,7 +2,7 @@
  * Finger Sensing Pad PS/2 mouse driver.
  *
  * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
- * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation.
+ * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
  *
  *   This program is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU General Public License
 #define        FSP_BIT_FIX_HSCR        BIT(5)
 #define        FSP_BIT_DRAG_LOCK       BIT(6)
 
+#define        FSP_REG_SWC1            (0x90)
+#define        FSP_BIT_SWC1_EN_ABS_1F  BIT(0)
+#define        FSP_BIT_SWC1_EN_GID     BIT(1)
+#define        FSP_BIT_SWC1_EN_ABS_2F  BIT(2)
+#define        FSP_BIT_SWC1_EN_FUP_OUT BIT(3)
+#define        FSP_BIT_SWC1_EN_ABS_CON BIT(4)
+#define        FSP_BIT_SWC1_GST_GRP0   BIT(5)
+#define        FSP_BIT_SWC1_GST_GRP1   BIT(6)
+#define        FSP_BIT_SWC1_BX_COMPAT  BIT(7)
+
 /* Finger-sensing Pad packet formating related definitions */
 
 /* absolute packet type */
 #define        FSP_PKT_TYPE_NORMAL_OPC (0x03)
 #define        FSP_PKT_TYPE_SHIFT      (6)
 
+/* bit definitions for the first byte of report packet */
+#define        FSP_PB0_LBTN            BIT(0)
+#define        FSP_PB0_RBTN            BIT(1)
+#define        FSP_PB0_MBTN            BIT(2)
+#define        FSP_PB0_MFMC_FGR2       FSP_PB0_MBTN
+#define        FSP_PB0_MUST_SET        BIT(3)
+#define        FSP_PB0_PHY_BTN         BIT(4)
+#define        FSP_PB0_MFMC            BIT(5)
+
+/* hardware revisions */
+#define        FSP_VER_STL3888_A4      (0xC1)
+#define        FSP_VER_STL3888_B0      (0xD0)
+#define        FSP_VER_STL3888_B1      (0xD1)
+#define        FSP_VER_STL3888_B2      (0xD2)
+#define        FSP_VER_STL3888_C0      (0xE0)
+#define        FSP_VER_STL3888_C1      (0xE1)
+#define        FSP_VER_STL3888_D0      (0xE2)
+#define        FSP_VER_STL3888_D1      (0xE3)
+#define        FSP_VER_STL3888_E0      (0xE4)
+
 #ifdef __KERNEL__
 
 struct fsp_data {
        unsigned char   ver;            /* hardware version */
        unsigned char   rev;            /* hardware revison */
-       unsigned char   buttons;        /* Number of buttons */
+       unsigned int    buttons;        /* Number of buttons */
        unsigned int    flags;
 #define        FSPDRV_FLAG_EN_OPC      (0x001) /* enable on-pad clicking */
 
@@ -78,6 +108,7 @@ struct fsp_data {
 
        unsigned char   last_reg;       /* Last register we requested read from */
        unsigned char   last_val;
+       unsigned int    last_mt_fgr;    /* Last seen finger(multitouch) */
 };
 
 #ifdef CONFIG_MOUSE_PS2_SENTELIC
index bd5b10eeeb407d595bab57117a1e321ea2d26ee9..f5fbdf94de3bee3e0f3f27337dcaffab521bf457 100644 (file)
@@ -184,7 +184,7 @@ module_init(ams_delta_serio_init);
 static void __exit ams_delta_serio_exit(void)
 {
        serio_unregister_port(ams_delta_serio);
-       free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
+       free_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
        gpio_free_array(ams_delta_gpios,
                        ARRAY_SIZE(ams_delta_gpios));
 }
index e53f4081a586956974bea8731d366366515ff6f8..bed7cbf84cfd513a2a05b7553c3318698e3d5d7e 100644 (file)
@@ -76,6 +76,7 @@ config TABLET_USB_KBTAB
 config TABLET_USB_WACOM
        tristate "Wacom Intuos/Graphire tablet support (USB)"
        depends on USB_ARCH_HAS_HCD
+       select POWER_SUPPLY
        select USB
        select NEW_LEDS
        select LEDS_CLASS
index 0783864a7dc2e1c77ad1aada99841ceab27106b3..b4842d0e61dd4bdd0e0c4aaa5b4305c61e82e63d 100644 (file)
@@ -88,6 +88,7 @@
 #include <linux/mod_devicetable.h>
 #include <linux/init.h>
 #include <linux/usb/input.h>
+#include <linux/power_supply.h>
 #include <asm/unaligned.h>
 
 /*
@@ -112,6 +113,7 @@ struct wacom {
        struct urb *irq;
        struct wacom_wac wacom_wac;
        struct mutex lock;
+       struct work_struct work;
        bool open;
        char phys[32];
        struct wacom_led {
@@ -120,8 +122,15 @@ struct wacom {
                u8 hlv;       /* status led brightness button pressed (1..127) */
                u8 img_lum;   /* OLED matrix display brightness */
        } led;
+       struct power_supply battery;
 };
 
+static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
+{
+       struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+       schedule_work(&wacom->work);
+}
+
 extern const struct usb_device_id wacom_ids[];
 
 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
index ca28066dc81e335bb85483617a69f46056dbd63e..0d269212931e86be95d4c77c26b667ac781200c2 100644 (file)
@@ -167,6 +167,19 @@ static void wacom_close(struct input_dev *dev)
                usb_autopm_put_interface(wacom->intf);
 }
 
+/*
+ * Static values for max X/Y and resolution of Pen interface is stored in
+ * features. This mean physical size of active area can be computed.
+ * This is useful to do when Pen and Touch have same active area of tablet.
+ * This means for Touch device, we only need to find max X/Y value and we
+ * have enough information to compute resolution of touch.
+ */
+static void wacom_set_phy_from_res(struct wacom_features *features)
+{
+       features->x_phy = (features->x_max * 100) / features->x_resolution;
+       features->y_phy = (features->y_max * 100) / features->y_resolution;
+}
+
 static int wacom_parse_logical_collection(unsigned char *report,
                                          struct wacom_features *features)
 {
@@ -178,15 +191,7 @@ static int wacom_parse_logical_collection(unsigned char *report,
                features->pktlen = WACOM_PKGLEN_BBTOUCH3;
                features->device_type = BTN_TOOL_FINGER;
 
-               /*
-                * Stylus and Touch have same active area
-                * so compute physical size based on stylus
-                * data before its overwritten.
-                */
-               features->x_phy =
-                       (features->x_max * 100) / features->x_resolution;
-               features->y_phy =
-                       (features->y_max * 100) / features->y_resolution;
+               wacom_set_phy_from_res(features);
 
                features->x_max = features->y_max =
                        get_unaligned_le16(&report[10]);
@@ -422,6 +427,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
                                                report_id, rep_data, 4, 1);
                } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
        } else if (features->type != TABLETPC &&
+                  features->type != WIRELESS &&
                   features->device_type == BTN_TOOL_PEN) {
                do {
                        rep_data[0] = 2;
@@ -454,6 +460,21 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
        features->pressure_fuzz = 0;
        features->distance_fuzz = 0;
 
+       /*
+        * The wireless device HID is basic and layout conflicts with
+        * other tablets (monitor and touch interface can look like pen).
+        * Skip the query for this type and modify defaults based on
+        * interface number.
+        */
+       if (features->type == WIRELESS) {
+               if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+                       features->device_type = 0;
+               } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
+                       features->device_type = BTN_TOOL_DOUBLETAP;
+                       features->pktlen = WACOM_PKGLEN_BBTOUCH3;
+               }
+       }
+
        /* only Tablet PCs and Bamboo P&T need to retrieve the info */
        if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
            (features->type != BAMBOO_PT))
@@ -822,6 +843,152 @@ static void wacom_destroy_leds(struct wacom *wacom)
        }
 }
 
+static enum power_supply_property wacom_battery_props[] = {
+       POWER_SUPPLY_PROP_CAPACITY
+};
+
+static int wacom_battery_get_property(struct power_supply *psy,
+                                     enum power_supply_property psp,
+                                     union power_supply_propval *val)
+{
+       struct wacom *wacom = container_of(psy, struct wacom, battery);
+       int ret = 0;
+
+       switch (psp) {
+               case POWER_SUPPLY_PROP_CAPACITY:
+                       val->intval =
+                               wacom->wacom_wac.battery_capacity * 100 / 31;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
+       }
+
+       return ret;
+}
+
+static int wacom_initialize_battery(struct wacom *wacom)
+{
+       int error = 0;
+
+       if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) {
+               wacom->battery.properties = wacom_battery_props;
+               wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
+               wacom->battery.get_property = wacom_battery_get_property;
+               wacom->battery.name = "wacom_battery";
+               wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+               wacom->battery.use_for_apm = 0;
+
+               error = power_supply_register(&wacom->usbdev->dev,
+                                             &wacom->battery);
+       }
+
+       return error;
+}
+
+static void wacom_destroy_battery(struct wacom *wacom)
+{
+       if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR)
+               power_supply_unregister(&wacom->battery);
+}
+
+static int wacom_register_input(struct wacom *wacom)
+{
+       struct input_dev *input_dev;
+       struct usb_interface *intf = wacom->intf;
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
+       int error;
+
+       input_dev = input_allocate_device();
+       if (!input_dev)
+               return -ENOMEM;
+
+       input_dev->name = wacom_wac->name;
+       input_dev->dev.parent = &intf->dev;
+       input_dev->open = wacom_open;
+       input_dev->close = wacom_close;
+       usb_to_input_id(dev, &input_dev->id);
+       input_set_drvdata(input_dev, wacom);
+
+       wacom_wac->input = input_dev;
+       wacom_setup_input_capabilities(input_dev, wacom_wac);
+
+       error = input_register_device(input_dev);
+       if (error) {
+               input_free_device(input_dev);
+               wacom_wac->input = NULL;
+       }
+
+       return error;
+}
+
+static void wacom_wireless_work(struct work_struct *work)
+{
+       struct wacom *wacom = container_of(work, struct wacom, work);
+       struct usb_device *usbdev = wacom->usbdev;
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+       /*
+        * Regardless if this is a disconnect or a new tablet,
+        * remove any existing input devices.
+        */
+
+       /* Stylus interface */
+       wacom = usb_get_intfdata(usbdev->config->interface[1]);
+       if (wacom->wacom_wac.input)
+               input_unregister_device(wacom->wacom_wac.input);
+       wacom->wacom_wac.input = 0;
+
+       /* Touch interface */
+       wacom = usb_get_intfdata(usbdev->config->interface[2]);
+       if (wacom->wacom_wac.input)
+               input_unregister_device(wacom->wacom_wac.input);
+       wacom->wacom_wac.input = 0;
+
+       if (wacom_wac->pid == 0) {
+               printk(KERN_INFO "wacom: wireless tablet disconnected\n");
+       } else {
+               const struct usb_device_id *id = wacom_ids;
+
+               printk(KERN_INFO
+                      "wacom: wireless tablet connected with PID %x\n",
+                      wacom_wac->pid);
+
+               while (id->match_flags) {
+                       if (id->idVendor == USB_VENDOR_ID_WACOM &&
+                           id->idProduct == wacom_wac->pid)
+                               break;
+                       id++;
+               }
+
+               if (!id->match_flags) {
+                       printk(KERN_INFO
+                              "wacom: ignorning unknown PID.\n");
+                       return;
+               }
+
+               /* Stylus interface */
+               wacom = usb_get_intfdata(usbdev->config->interface[1]);
+               wacom_wac = &wacom->wacom_wac;
+               wacom_wac->features =
+                       *((struct wacom_features *)id->driver_info);
+               wacom_wac->features.device_type = BTN_TOOL_PEN;
+               wacom_register_input(wacom);
+
+               /* Touch interface */
+               wacom = usb_get_intfdata(usbdev->config->interface[2]);
+               wacom_wac = &wacom->wacom_wac;
+               wacom_wac->features =
+                       *((struct wacom_features *)id->driver_info);
+               wacom_wac->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
+               wacom_wac->features.device_type = BTN_TOOL_FINGER;
+               wacom_set_phy_from_res(&wacom_wac->features);
+               wacom_wac->features.x_max = wacom_wac->features.y_max = 4096;
+               wacom_register_input(wacom);
+       }
+}
+
 static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct usb_device *dev = interface_to_usbdev(intf);
@@ -829,18 +996,14 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        struct wacom *wacom;
        struct wacom_wac *wacom_wac;
        struct wacom_features *features;
-       struct input_dev *input_dev;
        int error;
 
        if (!id->driver_info)
                return -EINVAL;
 
        wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
-       input_dev = input_allocate_device();
-       if (!wacom || !input_dev) {
-               error = -ENOMEM;
-               goto fail1;
-       }
+       if (!wacom)
+               return -ENOMEM;
 
        wacom_wac = &wacom->wacom_wac;
        wacom_wac->features = *((struct wacom_features *)id->driver_info);
@@ -866,11 +1029,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        wacom->usbdev = dev;
        wacom->intf = intf;
        mutex_init(&wacom->lock);
+       INIT_WORK(&wacom->work, wacom_wireless_work);
        usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
        strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
 
-       wacom_wac->input = input_dev;
-
        endpoint = &intf->cur_altsetting->endpoint[0].desc;
 
        /* Retrieve the physical and logical size for OEM devices */
@@ -894,15 +1056,6 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
                        goto fail3;
        }
 
-       input_dev->name = wacom_wac->name;
-       input_dev->dev.parent = &intf->dev;
-       input_dev->open = wacom_open;
-       input_dev->close = wacom_close;
-       usb_to_input_id(dev, &input_dev->id);
-       input_set_drvdata(input_dev, wacom);
-
-       wacom_setup_input_capabilities(input_dev, wacom_wac);
-
        usb_fill_int_urb(wacom->irq, dev,
                         usb_rcvintpipe(dev, endpoint->bEndpointAddress),
                         wacom_wac->data, features->pktlen,
@@ -914,22 +1067,34 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        if (error)
                goto fail4;
 
-       error = input_register_device(input_dev);
+       error = wacom_initialize_battery(wacom);
        if (error)
                goto fail5;
 
+       if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
+               error = wacom_register_input(wacom);
+               if (error)
+                       goto fail6;
+       }
+
        /* Note that if query fails it is not a hard failure */
        wacom_query_tablet_data(intf, features);
 
        usb_set_intfdata(intf, wacom);
+
+       if (features->quirks & WACOM_QUIRK_MONITOR) {
+               if (usb_submit_urb(wacom->irq, GFP_KERNEL))
+                       goto fail5;
+       }
+
        return 0;
 
+ fail6: wacom_destroy_battery(wacom);
  fail5: wacom_destroy_leds(wacom);
  fail4:        wacom_remove_shared_data(wacom_wac);
  fail3:        usb_free_urb(wacom->irq);
  fail2:        usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
- fail1:        input_free_device(input_dev);
-       kfree(wacom);
+ fail1:        kfree(wacom);
        return error;
 }
 
@@ -940,7 +1105,10 @@ static void wacom_disconnect(struct usb_interface *intf)
        usb_set_intfdata(intf, NULL);
 
        usb_kill_urb(wacom->irq);
-       input_unregister_device(wacom->wacom_wac.input);
+       cancel_work_sync(&wacom->work);
+       if (wacom->wacom_wac.input)
+               input_unregister_device(wacom->wacom_wac.input);
+       wacom_destroy_battery(wacom);
        wacom_destroy_leds(wacom);
        usb_free_urb(wacom->irq);
        usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
@@ -972,7 +1140,8 @@ static int wacom_resume(struct usb_interface *intf)
        wacom_query_tablet_data(intf, features);
        wacom_led_control(wacom);
 
-       if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
+       if ((wacom->open || features->quirks & WACOM_QUIRK_MONITOR)
+            && usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
                rv = -EIO;
 
        mutex_unlock(&wacom->lock);
index 89a96427faa0285ea6dd16180e9eda41ea2e3ece..cecd35c8f0b3ce46cb82bd51514f7b468ab282cd 100644 (file)
@@ -1044,6 +1044,35 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
        return 0;
 }
 
+static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
+{
+       unsigned char *data = wacom->data;
+       int connected;
+
+       if (len != WACOM_PKGLEN_WIRELESS || data[0] != 0x80)
+               return 0;
+
+       connected = data[1] & 0x01;
+       if (connected) {
+               int pid, battery;
+
+               pid = get_unaligned_be16(&data[6]);
+               battery = data[5] & 0x3f;
+               if (wacom->pid != pid) {
+                       wacom->pid = pid;
+                       wacom_schedule_work(wacom);
+               }
+               wacom->battery_capacity = battery;
+       } else if (wacom->pid != 0) {
+               /* disconnected while previously connected */
+               wacom->pid = 0;
+               wacom_schedule_work(wacom);
+               wacom->battery_capacity = 0;
+       }
+
+       return 0;
+}
+
 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
 {
        bool sync;
@@ -1094,6 +1123,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
                sync = wacom_bpt_irq(wacom_wac, len);
                break;
 
+       case WIRELESS:
+               sync = wacom_wireless_irq(wacom_wac, len);
+               break;
+
        default:
                sync = false;
                break;
@@ -1155,7 +1188,7 @@ void wacom_setup_device_quirks(struct wacom_features *features)
 
        /* these device have multiple inputs */
        if (features->type == TABLETPC || features->type == TABLETPC2FG ||
-           features->type == BAMBOO_PT)
+           features->type == BAMBOO_PT || features->type == WIRELESS)
                features->quirks |= WACOM_QUIRK_MULTI_INPUT;
 
        /* quirk for bamboo touch with 2 low res touches */
@@ -1167,6 +1200,16 @@ void wacom_setup_device_quirks(struct wacom_features *features)
                features->y_fuzz <<= 5;
                features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
        }
+
+       if (features->type == WIRELESS) {
+
+               /* monitor never has input and pen/touch have delayed create */
+               features->quirks |= WACOM_QUIRK_NO_INPUT;
+
+               /* must be monitor interface if no device_type set */
+               if (!features->device_type)
+                       features->quirks |= WACOM_QUIRK_MONITOR;
+       }
 }
 
 static unsigned int wacom_calculate_touch_res(unsigned int logical_max,
@@ -1640,6 +1683,9 @@ static const struct wacom_features wacom_features_0xEC =
 static const struct wacom_features wacom_features_0x47 =
        { "Wacom Intuos2 6x8",    WACOM_PKGLEN_INTUOS,    20320, 16240, 1023,
          31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x84 =
+       { "Wacom Wireless Receiver", WACOM_PKGLEN_WIRELESS, 0, 0, 0,
+         0, WIRELESS, 0, 0 };
 static const struct wacom_features wacom_features_0xD0 =
        { "Wacom Bamboo 2FG",     WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
          31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -1766,6 +1812,7 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID,
                              USB_INTERFACE_SUBCLASS_BOOT,
                              USB_INTERFACE_PROTOCOL_MOUSE) },
+       { USB_DEVICE_WACOM(0x84) },
        { USB_DEVICE_WACOM(0xD0) },
        { USB_DEVICE_WACOM(0xD1) },
        { USB_DEVICE_WACOM(0xD2) },
index 4f0ba21b01962c4b2556535468c96f7355e17d1c..ba5a334e54d6b525995965cad49acba64a41e89b 100644 (file)
@@ -24,6 +24,7 @@
 #define WACOM_PKGLEN_BBTOUCH   20
 #define WACOM_PKGLEN_BBTOUCH3  64
 #define WACOM_PKGLEN_BBPEN     10
+#define WACOM_PKGLEN_WIRELESS  32
 
 /* device IDs */
 #define STYLUS_DEVICE_ID       0x02
@@ -45,6 +46,8 @@
 /* device quirks */
 #define WACOM_QUIRK_MULTI_INPUT                0x0001
 #define WACOM_QUIRK_BBTOUCH_LOWRES     0x0002
+#define WACOM_QUIRK_NO_INPUT           0x0004
+#define WACOM_QUIRK_MONITOR            0x0008
 
 enum {
        PENPARTNER = 0,
@@ -54,6 +57,7 @@ enum {
        PL,
        DTU,
        BAMBOO_PT,
+       WIRELESS,
        INTUOS,
        INTUOS3S,
        INTUOS3,
@@ -107,6 +111,8 @@ struct wacom_wac {
        struct wacom_features features;
        struct wacom_shared *shared;
        struct input_dev *input;
+       int pid;
+       int battery_capacity;
 };
 
 #endif
index 65f36cf2ff334bf0059ddd57401e5b70c19566e5..b0f2ef9881883df42f5513eb3f1f0479e6c83653 100644 (file)
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/module.h>
+#include <linux/fsl/mxs-dma.h>
 
 #include <mach/mxs.h>
 #include <mach/common.h>
-#include <mach/dma.h>
 #include <mach/mmc.h>
 
 #define DRIVER_NAME    "mxs-mmc"
@@ -305,7 +305,7 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
 }
 
 static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
-       struct mxs_mmc_host *host, unsigned int append)
+       struct mxs_mmc_host *host, unsigned long flags)
 {
        struct dma_async_tx_descriptor *desc;
        struct mmc_data *data = host->data;
@@ -325,7 +325,7 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
        }
 
        desc = dmaengine_prep_slave_sg(host->dmach,
-                               sgl, sg_len, host->slave_dirn, append);
+                               sgl, sg_len, host->slave_dirn, flags);
        if (desc) {
                desc->callback = mxs_mmc_dma_irq_callback;
                desc->callback_param = host;
@@ -358,7 +358,7 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host)
        host->ssp_pio_words[2] = cmd1;
        host->dma_dir = DMA_NONE;
        host->slave_dirn = DMA_TRANS_NONE;
-       desc = mxs_mmc_prep_dma(host, 0);
+       desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
        if (!desc)
                goto out;
 
@@ -398,7 +398,7 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
        host->ssp_pio_words[2] = cmd1;
        host->dma_dir = DMA_NONE;
        host->slave_dirn = DMA_TRANS_NONE;
-       desc = mxs_mmc_prep_dma(host, 0);
+       desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
        if (!desc)
                goto out;
 
@@ -526,7 +526,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
        host->data = data;
        host->dma_dir = dma_data_dir;
        host->slave_dirn = slave_dirn;
-       desc = mxs_mmc_prep_dma(host, 1);
+       desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!desc)
                goto out;
 
index 284cf3433720fe07233ce5c380e3668999d4b94e..5760c1a4b3f66ea3125b71d28ab4f62ceb4ee925 100644 (file)
@@ -304,9 +304,6 @@ config MTD_OOPS
          buffer in a flash partition where it can be read back at some
          later point.
 
-         To use, add console=ttyMTDx to the kernel command line,
-         where x is the MTD device number to use.
-
 config MTD_SWAP
        tristate "Swap on MTD device support"
        depends on MTD && SWAP
index 9bcd1f415f43f137631793e8371ffb6899b5d92c..dbbd2edfb812805e96c95ea7530aa229c821d78c 100644 (file)
@@ -87,7 +87,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **
 
 static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
                     size_t *retlen, void **virt, resource_size_t *phys);
-static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
+static int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
 
 static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
 static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
@@ -262,9 +262,9 @@ static void fixup_st_m28w320cb(struct mtd_info *mtd)
 static void fixup_use_point(struct mtd_info *mtd)
 {
        struct map_info *map = mtd->priv;
-       if (!mtd->point && map_is_linear(map)) {
-               mtd->point   = cfi_intelext_point;
-               mtd->unpoint = cfi_intelext_unpoint;
+       if (!mtd->_point && map_is_linear(map)) {
+               mtd->_point   = cfi_intelext_point;
+               mtd->_unpoint = cfi_intelext_unpoint;
        }
 }
 
@@ -274,8 +274,8 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
        struct cfi_private *cfi = map->fldrv_priv;
        if (cfi->cfiq->BufWriteTimeoutTyp) {
                printk(KERN_INFO "Using buffer write method\n" );
-               mtd->write = cfi_intelext_write_buffers;
-               mtd->writev = cfi_intelext_writev;
+               mtd->_write = cfi_intelext_write_buffers;
+               mtd->_writev = cfi_intelext_writev;
        }
 }
 
@@ -443,15 +443,15 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
        mtd->type = MTD_NORFLASH;
 
        /* Fill in the default mtd operations */
-       mtd->erase   = cfi_intelext_erase_varsize;
-       mtd->read    = cfi_intelext_read;
-       mtd->write   = cfi_intelext_write_words;
-       mtd->sync    = cfi_intelext_sync;
-       mtd->lock    = cfi_intelext_lock;
-       mtd->unlock  = cfi_intelext_unlock;
-       mtd->is_locked = cfi_intelext_is_locked;
-       mtd->suspend = cfi_intelext_suspend;
-       mtd->resume  = cfi_intelext_resume;
+       mtd->_erase   = cfi_intelext_erase_varsize;
+       mtd->_read    = cfi_intelext_read;
+       mtd->_write   = cfi_intelext_write_words;
+       mtd->_sync    = cfi_intelext_sync;
+       mtd->_lock    = cfi_intelext_lock;
+       mtd->_unlock  = cfi_intelext_unlock;
+       mtd->_is_locked = cfi_intelext_is_locked;
+       mtd->_suspend = cfi_intelext_suspend;
+       mtd->_resume  = cfi_intelext_resume;
        mtd->flags   = MTD_CAP_NORFLASH;
        mtd->name    = map->name;
        mtd->writesize = 1;
@@ -600,12 +600,12 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
        }
 
 #ifdef CONFIG_MTD_OTP
-       mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
-       mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
-       mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg;
-       mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
-       mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info;
-       mtd->get_user_prot_info = cfi_intelext_get_user_prot_info;
+       mtd->_read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
+       mtd->_read_user_prot_reg = cfi_intelext_read_user_prot_reg;
+       mtd->_write_user_prot_reg = cfi_intelext_write_user_prot_reg;
+       mtd->_lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
+       mtd->_get_fact_prot_info = cfi_intelext_get_fact_prot_info;
+       mtd->_get_user_prot_info = cfi_intelext_get_user_prot_info;
 #endif
 
        /* This function has the potential to distort the reality
@@ -1017,8 +1017,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
        case FL_READY:
        case FL_STATUS:
        case FL_JEDEC_QUERY:
-               /* We should really make set_vpp() count, rather than doing this */
-               DISABLE_VPP(map);
                break;
        default:
                printk(KERN_ERR "%s: put_chip() called with oldstate %d!!\n", map->name, chip->oldstate);
@@ -1324,7 +1322,7 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
        int chipnum;
        int ret = 0;
 
-       if (!map->virt || (from + len > mtd->size))
+       if (!map->virt)
                return -EINVAL;
 
        /* Now lock the chip(s) to POINT state */
@@ -1334,7 +1332,6 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
        ofs = from - (chipnum << cfi->chipshift);
 
        *virt = map->virt + cfi->chips[chipnum].start + ofs;
-       *retlen = 0;
        if (phys)
                *phys = map->phys + cfi->chips[chipnum].start + ofs;
 
@@ -1369,12 +1366,12 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
        return 0;
 }
 
-static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 {
        struct map_info *map = mtd->priv;
        struct cfi_private *cfi = map->fldrv_priv;
        unsigned long ofs;
-       int chipnum;
+       int chipnum, err = 0;
 
        /* Now unlock the chip(s) POINT state */
 
@@ -1382,7 +1379,7 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
        chipnum = (from >> cfi->chipshift);
        ofs = from - (chipnum <<  cfi->chipshift);
 
-       while (len) {
+       while (len && !err) {
                unsigned long thislen;
                struct flchip *chip;
 
@@ -1400,8 +1397,10 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
                        chip->ref_point_counter--;
                        if(chip->ref_point_counter == 0)
                                chip->state = FL_READY;
-               } else
-                       printk(KERN_ERR "%s: Warning: unpoint called on non pointed region\n", map->name); /* Should this give an error? */
+               } else {
+                       printk(KERN_ERR "%s: Error: unpoint called on non pointed region\n", map->name);
+                       err = -EINVAL;
+               }
 
                put_chip(map, chip, chip->start);
                mutex_unlock(&chip->mutex);
@@ -1410,6 +1409,8 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
                ofs = 0;
                chipnum++;
        }
+
+       return err;
 }
 
 static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
@@ -1456,8 +1457,6 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz
        chipnum = (from >> cfi->chipshift);
        ofs = from - (chipnum <<  cfi->chipshift);
 
-       *retlen = 0;
-
        while (len) {
                unsigned long thislen;
 
@@ -1551,7 +1550,8 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
        }
 
        xip_enable(map, chip, adr);
- out:  put_chip(map, chip, adr);
+ out:  DISABLE_VPP(map);
+       put_chip(map, chip, adr);
        mutex_unlock(&chip->mutex);
        return ret;
 }
@@ -1565,10 +1565,6 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
        int chipnum;
        unsigned long ofs;
 
-       *retlen = 0;
-       if (!len)
-               return 0;
-
        chipnum = to >> cfi->chipshift;
        ofs = to  - (chipnum << cfi->chipshift);
 
@@ -1794,7 +1790,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
        }
 
        xip_enable(map, chip, cmd_adr);
- out:  put_chip(map, chip, cmd_adr);
+ out:  DISABLE_VPP(map);
+       put_chip(map, chip, cmd_adr);
        mutex_unlock(&chip->mutex);
        return ret;
 }
@@ -1813,7 +1810,6 @@ static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs,
        for (i = 0; i < count; i++)
                len += vecs[i].iov_len;
 
-       *retlen = 0;
        if (!len)
                return 0;
 
@@ -1932,6 +1928,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
                        ret = -EIO;
                } else if (chipstatus & 0x20 && retries--) {
                        printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus);
+                       DISABLE_VPP(map);
                        put_chip(map, chip, adr);
                        mutex_unlock(&chip->mutex);
                        goto retry;
@@ -1944,7 +1941,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
        }
 
        xip_enable(map, chip, adr);
- out:  put_chip(map, chip, adr);
+ out:  DISABLE_VPP(map);
+       put_chip(map, chip, adr);
        mutex_unlock(&chip->mutex);
        return ret;
 }
@@ -2086,7 +2084,8 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
        }
 
        xip_enable(map, chip, adr);
-out:   put_chip(map, chip, adr);
+ out:  DISABLE_VPP(map);
+       put_chip(map, chip, adr);
        mutex_unlock(&chip->mutex);
        return ret;
 }
@@ -2483,7 +2482,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
                           allowed to. Or should we return -EAGAIN, because the upper layers
                           ought to have already shut down anything which was using the device
                           anyway? The latter for now. */
-                       printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->oldstate);
+                       printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->state);
                        ret = -EAGAIN;
                case FL_PM_SUSPENDED:
                        break;
index 8d70895a58d6b424d34563e7f43a2a13df57ddfb..d02592e6a0f02a89195ef1aea302445d98585b0e 100644 (file)
@@ -59,6 +59,9 @@ static void cfi_amdstd_resume (struct mtd_info *);
 static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *);
 static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 
+static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+                                 size_t *retlen, const u_char *buf);
+
 static void cfi_amdstd_destroy(struct mtd_info *);
 
 struct mtd_info *cfi_cmdset_0002(struct map_info *, int);
@@ -189,7 +192,7 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
        struct cfi_private *cfi = map->fldrv_priv;
        if (cfi->cfiq->BufWriteTimeoutTyp) {
                pr_debug("Using buffer write method\n" );
-               mtd->write = cfi_amdstd_write_buffers;
+               mtd->_write = cfi_amdstd_write_buffers;
        }
 }
 
@@ -228,8 +231,8 @@ static void fixup_convert_atmel_pri(struct mtd_info *mtd)
 static void fixup_use_secsi(struct mtd_info *mtd)
 {
        /* Setup for chips with a secsi area */
-       mtd->read_user_prot_reg = cfi_amdstd_secsi_read;
-       mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;
+       mtd->_read_user_prot_reg = cfi_amdstd_secsi_read;
+       mtd->_read_fact_prot_reg = cfi_amdstd_secsi_read;
 }
 
 static void fixup_use_erase_chip(struct mtd_info *mtd)
@@ -238,7 +241,7 @@ static void fixup_use_erase_chip(struct mtd_info *mtd)
        struct cfi_private *cfi = map->fldrv_priv;
        if ((cfi->cfiq->NumEraseRegions == 1) &&
                ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) {
-               mtd->erase = cfi_amdstd_erase_chip;
+               mtd->_erase = cfi_amdstd_erase_chip;
        }
 
 }
@@ -249,8 +252,8 @@ static void fixup_use_erase_chip(struct mtd_info *mtd)
  */
 static void fixup_use_atmel_lock(struct mtd_info *mtd)
 {
-       mtd->lock = cfi_atmel_lock;
-       mtd->unlock = cfi_atmel_unlock;
+       mtd->_lock = cfi_atmel_lock;
+       mtd->_unlock = cfi_atmel_unlock;
        mtd->flags |= MTD_POWERUP_LOCK;
 }
 
@@ -429,12 +432,12 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
        mtd->type = MTD_NORFLASH;
 
        /* Fill in the default mtd operations */
-       mtd->erase   = cfi_amdstd_erase_varsize;
-       mtd->write   = cfi_amdstd_write_words;
-       mtd->read    = cfi_amdstd_read;
-       mtd->sync    = cfi_amdstd_sync;
-       mtd->suspend = cfi_amdstd_suspend;
-       mtd->resume  = cfi_amdstd_resume;
+       mtd->_erase   = cfi_amdstd_erase_varsize;
+       mtd->_write   = cfi_amdstd_write_words;
+       mtd->_read    = cfi_amdstd_read;
+       mtd->_sync    = cfi_amdstd_sync;
+       mtd->_suspend = cfi_amdstd_suspend;
+       mtd->_resume  = cfi_amdstd_resume;
        mtd->flags   = MTD_CAP_NORFLASH;
        mtd->name    = map->name;
        mtd->writesize = 1;
@@ -443,6 +446,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
        pr_debug("MTD %s(): write buffer size %d\n", __func__,
                        mtd->writebufsize);
 
+       mtd->_panic_write = cfi_amdstd_panic_write;
        mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot;
 
        if (cfi->cfi_mode==CFI_MODE_CFI){
@@ -770,8 +774,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
 
        case FL_READY:
        case FL_STATUS:
-               /* We should really make set_vpp() count, rather than doing this */
-               DISABLE_VPP(map);
                break;
        default:
                printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);
@@ -1013,13 +1015,9 @@ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_
        int ret = 0;
 
        /* ofs: offset within the first chip that the first read should start */
-
        chipnum = (from >> cfi->chipshift);
        ofs = from - (chipnum <<  cfi->chipshift);
 
-
-       *retlen = 0;
-
        while (len) {
                unsigned long thislen;
 
@@ -1097,16 +1095,11 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len,
        int chipnum;
        int ret = 0;
 
-
        /* ofs: offset within the first chip that the first read should start */
-
        /* 8 secsi bytes per chip */
        chipnum=from>>3;
        ofs=from & 7;
 
-
-       *retlen = 0;
-
        while (len) {
                unsigned long thislen;
 
@@ -1234,6 +1227,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
        xip_enable(map, chip, adr);
  op_done:
        chip->state = FL_READY;
+       DISABLE_VPP(map);
        put_chip(map, chip, adr);
        mutex_unlock(&chip->mutex);
 
@@ -1251,10 +1245,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
        unsigned long ofs, chipstart;
        DECLARE_WAITQUEUE(wait, current);
 
-       *retlen = 0;
-       if (!len)
-               return 0;
-
        chipnum = to >> cfi->chipshift;
        ofs = to  - (chipnum << cfi->chipshift);
        chipstart = cfi->chips[chipnum].start;
@@ -1476,6 +1466,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
        ret = -EIO;
  op_done:
        chip->state = FL_READY;
+       DISABLE_VPP(map);
        put_chip(map, chip, adr);
        mutex_unlock(&chip->mutex);
 
@@ -1493,10 +1484,6 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
        int chipnum;
        unsigned long ofs;
 
-       *retlen = 0;
-       if (!len)
-               return 0;
-
        chipnum = to >> cfi->chipshift;
        ofs = to  - (chipnum << cfi->chipshift);
 
@@ -1562,6 +1549,238 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
        return 0;
 }
 
+/*
+ * Wait for the flash chip to become ready to write data
+ *
+ * This is only called during the panic_write() path. When panic_write()
+ * is called, the kernel is in the process of a panic, and will soon be
+ * dead. Therefore we don't take any locks, and attempt to get access
+ * to the chip as soon as possible.
+ */
+static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
+                                unsigned long adr)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+       int retries = 10;
+       int i;
+
+       /*
+        * If the driver thinks the chip is idle, and no toggle bits
+        * are changing, then the chip is actually idle for sure.
+        */
+       if (chip->state == FL_READY && chip_ready(map, adr))
+               return 0;
+
+       /*
+        * Try several times to reset the chip and then wait for it
+        * to become idle. The upper limit of a few milliseconds of
+        * delay isn't a big problem: the kernel is dying anyway. It
+        * is more important to save the messages.
+        */
+       while (retries > 0) {
+               const unsigned long timeo = (HZ / 1000) + 1;
+
+               /* send the reset command */
+               map_write(map, CMD(0xF0), chip->start);
+
+               /* wait for the chip to become ready */
+               for (i = 0; i < jiffies_to_usecs(timeo); i++) {
+                       if (chip_ready(map, adr))
+                               return 0;
+
+                       udelay(1);
+               }
+       }
+
+       /* the chip never became ready */
+       return -EBUSY;
+}
+
+/*
+ * Write out one word of data to a single flash chip during a kernel panic
+ *
+ * This is only called during the panic_write() path. When panic_write()
+ * is called, the kernel is in the process of a panic, and will soon be
+ * dead. Therefore we don't take any locks, and attempt to get access
+ * to the chip as soon as possible.
+ *
+ * The implementation of this routine is intentionally similar to
+ * do_write_oneword(), in order to ease code maintenance.
+ */
+static int do_panic_write_oneword(struct map_info *map, struct flchip *chip,
+                                 unsigned long adr, map_word datum)
+{
+       const unsigned long uWriteTimeout = (HZ / 1000) + 1;
+       struct cfi_private *cfi = map->fldrv_priv;
+       int retry_cnt = 0;
+       map_word oldd;
+       int ret = 0;
+       int i;
+
+       adr += chip->start;
+
+       ret = cfi_amdstd_panic_wait(map, chip, adr);
+       if (ret)
+               return ret;
+
+       pr_debug("MTD %s(): PANIC WRITE 0x%.8lx(0x%.8lx)\n",
+                       __func__, adr, datum.x[0]);
+
+       /*
+        * Check for a NOP for the case when the datum to write is already
+        * present - it saves time and works around buggy chips that corrupt
+        * data at other locations when 0xff is written to a location that
+        * already contains 0xff.
+        */
+       oldd = map_read(map, adr);
+       if (map_word_equal(map, oldd, datum)) {
+               pr_debug("MTD %s(): NOP\n", __func__);
+               goto op_done;
+       }
+
+       ENABLE_VPP(map);
+
+retry:
+       cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+       map_write(map, datum, adr);
+
+       for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) {
+               if (chip_ready(map, adr))
+                       break;
+
+               udelay(1);
+       }
+
+       if (!chip_good(map, adr, datum)) {
+               /* reset on all failures. */
+               map_write(map, CMD(0xF0), chip->start);
+               /* FIXME - should have reset delay before continuing */
+
+               if (++retry_cnt <= MAX_WORD_RETRIES)
+                       goto retry;
+
+               ret = -EIO;
+       }
+
+op_done:
+       DISABLE_VPP(map);
+       return ret;
+}
+
+/*
+ * Write out some data during a kernel panic
+ *
+ * This is used by the mtdoops driver to save the dying messages from a
+ * kernel which has panic'd.
+ *
+ * This routine ignores all of the locking used throughout the rest of the
+ * driver, in order to ensure that the data gets written out no matter what
+ * state this driver (and the flash chip itself) was in when the kernel crashed.
+ *
+ * The implementation of this routine is intentionally similar to
+ * cfi_amdstd_write_words(), in order to ease code maintenance.
+ */
+static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+                                 size_t *retlen, const u_char *buf)
+{
+       struct map_info *map = mtd->priv;
+       struct cfi_private *cfi = map->fldrv_priv;
+       unsigned long ofs, chipstart;
+       int ret = 0;
+       int chipnum;
+
+       chipnum = to >> cfi->chipshift;
+       ofs = to - (chipnum << cfi->chipshift);
+       chipstart = cfi->chips[chipnum].start;
+
+       /* If it's not bus aligned, do the first byte write */
+       if (ofs & (map_bankwidth(map) - 1)) {
+               unsigned long bus_ofs = ofs & ~(map_bankwidth(map) - 1);
+               int i = ofs - bus_ofs;
+               int n = 0;
+               map_word tmp_buf;
+
+               ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], bus_ofs);
+               if (ret)
+                       return ret;
+
+               /* Load 'tmp_buf' with old contents of flash */
+               tmp_buf = map_read(map, bus_ofs + chipstart);
+
+               /* Number of bytes to copy from buffer */
+               n = min_t(int, len, map_bankwidth(map) - i);
+
+               tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
+
+               ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
+                                            bus_ofs, tmp_buf);
+               if (ret)
+                       return ret;
+
+               ofs += n;
+               buf += n;
+               (*retlen) += n;
+               len -= n;
+
+               if (ofs >> cfi->chipshift) {
+                       chipnum++;
+                       ofs = 0;
+                       if (chipnum == cfi->numchips)
+                               return 0;
+               }
+       }
+
+       /* We are now aligned, write as much as possible */
+       while (len >= map_bankwidth(map)) {
+               map_word datum;
+
+               datum = map_word_load(map, buf);
+
+               ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
+                                            ofs, datum);
+               if (ret)
+                       return ret;
+
+               ofs += map_bankwidth(map);
+               buf += map_bankwidth(map);
+               (*retlen) += map_bankwidth(map);
+               len -= map_bankwidth(map);
+
+               if (ofs >> cfi->chipshift) {
+                       chipnum++;
+                       ofs = 0;
+                       if (chipnum == cfi->numchips)
+                               return 0;
+
+                       chipstart = cfi->chips[chipnum].start;
+               }
+       }
+
+       /* Write the trailing bytes if any */
+       if (len & (map_bankwidth(map) - 1)) {
+               map_word tmp_buf;
+
+               ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], ofs);
+               if (ret)
+                       return ret;
+
+               tmp_buf = map_read(map, ofs + chipstart);
+
+               tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
+
+               ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
+                                            ofs, tmp_buf);
+               if (ret)
+                       return ret;
+
+               (*retlen) += len;
+       }
+
+       return 0;
+}
+
 
 /*
  * Handle devices with one erase region, that only implement
@@ -1649,6 +1868,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
 
        chip->state = FL_READY;
        xip_enable(map, chip, adr);
+       DISABLE_VPP(map);
        put_chip(map, chip, adr);
        mutex_unlock(&chip->mutex);
 
@@ -1739,6 +1959,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
        }
 
        chip->state = FL_READY;
+       DISABLE_VPP(map);
        put_chip(map, chip, adr);
        mutex_unlock(&chip->mutex);
        return ret;
index 85e80180b65b5131201d974e6946fdc658c2463b..096993f9711e1176e907685001260c8aaa39ff5b 100644 (file)
@@ -228,15 +228,15 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
                }
 
        /* Also select the correct geometry setup too */
-       mtd->erase = cfi_staa_erase_varsize;
-       mtd->read = cfi_staa_read;
-        mtd->write = cfi_staa_write_buffers;
-       mtd->writev = cfi_staa_writev;
-       mtd->sync = cfi_staa_sync;
-       mtd->lock = cfi_staa_lock;
-       mtd->unlock = cfi_staa_unlock;
-       mtd->suspend = cfi_staa_suspend;
-       mtd->resume = cfi_staa_resume;
+       mtd->_erase = cfi_staa_erase_varsize;
+       mtd->_read = cfi_staa_read;
+       mtd->_write = cfi_staa_write_buffers;
+       mtd->_writev = cfi_staa_writev;
+       mtd->_sync = cfi_staa_sync;
+       mtd->_lock = cfi_staa_lock;
+       mtd->_unlock = cfi_staa_unlock;
+       mtd->_suspend = cfi_staa_suspend;
+       mtd->_resume = cfi_staa_resume;
        mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE;
        mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
        mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
@@ -394,8 +394,6 @@ static int cfi_staa_read (struct mtd_info *mtd, loff_t from, size_t len, size_t
        chipnum = (from >> cfi->chipshift);
        ofs = from - (chipnum <<  cfi->chipshift);
 
-       *retlen = 0;
-
        while (len) {
                unsigned long thislen;
 
@@ -617,10 +615,6 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to,
        int chipnum;
        unsigned long ofs;
 
-       *retlen = 0;
-       if (!len)
-               return 0;
-
        chipnum = to >> cfi->chipshift;
        ofs = to  - (chipnum << cfi->chipshift);
 
@@ -904,12 +898,6 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd,
        int i, first;
        struct mtd_erase_region_info *regions = mtd->eraseregions;
 
-       if (instr->addr > mtd->size)
-               return -EINVAL;
-
-       if ((instr->len + instr->addr) > mtd->size)
-               return -EINVAL;
-
        /* Check that both start and end of the requested erase are
         * aligned with the erasesize at the appropriate addresses.
         */
@@ -1155,9 +1143,6 @@ static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        if (len & (mtd->erasesize -1))
                return -EINVAL;
 
-       if ((len + ofs) > mtd->size)
-               return -EINVAL;
-
        chipnum = ofs >> cfi->chipshift;
        adr = ofs - (chipnum << cfi->chipshift);
 
index 8e464054a631539d9478b659aabfb4a4dd6ae7f7..f992418f40a8d074277dd2798cb888bcc7fa404b 100644 (file)
@@ -173,12 +173,6 @@ int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
        int i, first;
        struct mtd_erase_region_info *regions = mtd->eraseregions;
 
-       if (ofs > mtd->size)
-               return -EINVAL;
-
-       if ((len + ofs) > mtd->size)
-               return -EINVAL;
-
        /* Check that both start and end of the requested erase are
         * aligned with the erasesize at the appropriate addresses.
         */
index 89c6595454a508925fe6c5accf0fbd44d1af56f9..800b0e853e868479b66b5dd917c602c428e0b3d7 100644 (file)
@@ -101,7 +101,7 @@ static void fixup_use_fwh_lock(struct mtd_info *mtd)
 {
        printk(KERN_NOTICE "using fwh lock/unlock method\n");
        /* Setup for the chips with the fwh lock method */
-       mtd->lock   = fwh_lock_varsize;
-       mtd->unlock = fwh_unlock_varsize;
+       mtd->_lock   = fwh_lock_varsize;
+       mtd->_unlock = fwh_unlock_varsize;
 }
 #endif /* FWH_LOCK_H */
index f2b87294687182abedba8ef30379fcd089e2827b..f7a5bca92aefed6d9eeac776b7eaf334d49efd53 100644 (file)
@@ -55,10 +55,10 @@ static struct mtd_info *map_absent_probe(struct map_info *map)
        mtd->name       = map->name;
        mtd->type       = MTD_ABSENT;
        mtd->size       = map->size;
-       mtd->erase      = map_absent_erase;
-       mtd->read       = map_absent_read;
-       mtd->write      = map_absent_write;
-       mtd->sync       = map_absent_sync;
+       mtd->_erase     = map_absent_erase;
+       mtd->_read      = map_absent_read;
+       mtd->_write     = map_absent_write;
+       mtd->_sync      = map_absent_sync;
        mtd->flags      = 0;
        mtd->erasesize  = PAGE_SIZE;
        mtd->writesize  = 1;
@@ -70,13 +70,11 @@ static struct mtd_info *map_absent_probe(struct map_info *map)
 
 static int map_absent_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
 {
-       *retlen = 0;
        return -ENODEV;
 }
 
 static int map_absent_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
 {
-       *retlen = 0;
        return -ENODEV;
 }
 
index 67640ccb2d4168d959de0f3c3d9c9b573f029923..991c2a1c05d364f33ac72cee2164f0ba6567e554 100644 (file)
@@ -64,11 +64,11 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
        mtd->name = map->name;
        mtd->type = MTD_RAM;
        mtd->size = map->size;
-       mtd->erase = mapram_erase;
-       mtd->get_unmapped_area = mapram_unmapped_area;
-       mtd->read = mapram_read;
-       mtd->write = mapram_write;
-       mtd->sync = mapram_nop;
+       mtd->_erase = mapram_erase;
+       mtd->_get_unmapped_area = mapram_unmapped_area;
+       mtd->_read = mapram_read;
+       mtd->_write = mapram_write;
+       mtd->_sync = mapram_nop;
        mtd->flags = MTD_CAP_RAM;
        mtd->writesize = 1;
 
@@ -122,14 +122,10 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
        unsigned long i;
 
        allff = map_word_ff(map);
-
        for (i=0; i<instr->len; i += map_bankwidth(map))
                map_write(map, allff, instr->addr + i);
-
        instr->state = MTD_ERASE_DONE;
-
        mtd_erase_callback(instr);
-
        return 0;
 }
 
index 593f73d480d2cf9e615ced4e2e66d758e3c4f8f3..47a43cf7e5c60fc162934b6da893b32dbd0cb717 100644 (file)
@@ -41,11 +41,11 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
        mtd->name = map->name;
        mtd->type = MTD_ROM;
        mtd->size = map->size;
-       mtd->get_unmapped_area = maprom_unmapped_area;
-       mtd->read = maprom_read;
-       mtd->write = maprom_write;
-       mtd->sync = maprom_nop;
-       mtd->erase = maprom_erase;
+       mtd->_get_unmapped_area = maprom_unmapped_area;
+       mtd->_read = maprom_read;
+       mtd->_write = maprom_write;
+       mtd->_sync = maprom_nop;
+       mtd->_erase = maprom_erase;
        mtd->flags = MTD_CAP_ROM;
        mtd->erasesize = map->size;
        mtd->writesize = 1;
@@ -85,8 +85,7 @@ static void maprom_nop(struct mtd_info *mtd)
 
 static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
 {
-       printk(KERN_NOTICE "maprom_write called\n");
-       return -EIO;
+       return -EROFS;
 }
 
 static int maprom_erase (struct mtd_info *mtd, struct erase_info *info)
index 8d3dac40d7e6e17411936a34a6e50b226ad78667..4cdb2af7bf44e74dbb72bab223d39beabd7c265a 100644 (file)
@@ -103,6 +103,13 @@ config M25PXX_USE_FAST_READ
        help
          This option enables FAST_READ access supported by ST M25Pxx.
 
+config MTD_SPEAR_SMI
+       tristate "SPEAR MTD NOR Support through SMI controller"
+       depends on PLAT_SPEAR
+       default y
+       help
+         This enable SNOR support on SPEAR platforms using SMI controller
+
 config MTD_SST25L
        tristate "Support SST25L (non JEDEC) SPI Flash chips"
        depends on SPI_MASTER
index 56c7cd462f11e33b191d73a6c3a7fe7d07e644b5..a4dd1d822b6c0db81a2bafd7c9ed6b790ca4ac2e 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_LART)                += lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)    += block2mtd.o
 obj-$(CONFIG_MTD_DATAFLASH)    += mtd_dataflash.o
 obj-$(CONFIG_MTD_M25P80)       += m25p80.o
+obj-$(CONFIG_MTD_SPEAR_SMI)    += spear_smi.o
 obj-$(CONFIG_MTD_SST25L)       += sst25l.o
 
 CFLAGS_docg3.o                 += -I$(src)
\ No newline at end of file
index e7e46d1e74631b6f31cbde50a0590077d8ee0da6..a4a80b742e65e99d602161002dd4296614e8593d 100644 (file)
@@ -104,14 +104,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
        int offset = from & (PAGE_SIZE-1);
        int cpylen;
 
-       if (from > mtd->size)
-               return -EINVAL;
-       if (from + len > mtd->size)
-               len = mtd->size - from;
-
-       if (retlen)
-               *retlen = 0;
-
        while (len) {
                if ((offset + len) > PAGE_SIZE)
                        cpylen = PAGE_SIZE - offset;    // multiple pages
@@ -148,8 +140,6 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
        int offset = to & ~PAGE_MASK;   // page offset
        int cpylen;
 
-       if (retlen)
-               *retlen = 0;
        while (len) {
                if ((offset+len) > PAGE_SIZE)
                        cpylen = PAGE_SIZE - offset;    // multiple pages
@@ -188,13 +178,6 @@ static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
        struct block2mtd_dev *dev = mtd->priv;
        int err;
 
-       if (!len)
-               return 0;
-       if (to >= mtd->size)
-               return -ENOSPC;
-       if (to + len > mtd->size)
-               len = mtd->size - to;
-
        mutex_lock(&dev->write_mutex);
        err = _block2mtd_write(dev, buf, to, len, retlen);
        mutex_unlock(&dev->write_mutex);
@@ -283,13 +266,14 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
        dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
        dev->mtd.erasesize = erase_size;
        dev->mtd.writesize = 1;
+       dev->mtd.writebufsize = PAGE_SIZE;
        dev->mtd.type = MTD_RAM;
        dev->mtd.flags = MTD_CAP_RAM;
-       dev->mtd.erase = block2mtd_erase;
-       dev->mtd.write = block2mtd_write;
-       dev->mtd.writev = mtd_writev;
-       dev->mtd.sync = block2mtd_sync;
-       dev->mtd.read = block2mtd_read;
+       dev->mtd._erase = block2mtd_erase;
+       dev->mtd._write = block2mtd_write;
+       dev->mtd._writev = mtd_writev;
+       dev->mtd._sync = block2mtd_sync;
+       dev->mtd._read = block2mtd_read;
        dev->mtd.priv = dev;
        dev->mtd.owner = THIS_MODULE;
 
index b1cdf647901960fd88ef2bce4a787c21ace8a03d..a4eb8b5b85ecb9b7db0c253622fc97720022f4f1 100644 (file)
@@ -562,14 +562,15 @@ void DoC2k_init(struct mtd_info *mtd)
 
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->writesize = 512;
+       mtd->writebufsize = mtd->writesize = 512;
        mtd->oobsize = 16;
+       mtd->ecc_strength = 2;
        mtd->owner = THIS_MODULE;
-       mtd->erase = doc_erase;
-       mtd->read = doc_read;
-       mtd->write = doc_write;
-       mtd->read_oob = doc_read_oob;
-       mtd->write_oob = doc_write_oob;
+       mtd->_erase = doc_erase;
+       mtd->_read = doc_read;
+       mtd->_write = doc_write;
+       mtd->_read_oob = doc_read_oob;
+       mtd->_write_oob = doc_write_oob;
        this->curfloor = -1;
        this->curchip = -1;
        mutex_init(&this->lock);
@@ -602,13 +603,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
        int i, len256 = 0, ret=0;
        size_t left = len;
 
-       /* Don't allow read past end of device */
-       if (from >= this->totlen)
-               return -EINVAL;
-
        mutex_lock(&this->lock);
-
-       *retlen = 0;
        while (left) {
                len = left;
 
@@ -748,13 +743,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
        size_t left = len;
        int status;
 
-       /* Don't allow write past end of device */
-       if (to >= this->totlen)
-               return -EINVAL;
-
        mutex_lock(&this->lock);
-
-       *retlen = 0;
        while (left) {
                len = left;
 
index 7543b98f46c46c1524a5f5aeec26b4c567e07d49..f6927955dab0d108385f853b5f6499a19961d333 100644 (file)
@@ -346,14 +346,15 @@ void DoCMil_init(struct mtd_info *mtd)
 
        /* FIXME: erase size is not always 8KiB */
        mtd->erasesize = 0x2000;
-       mtd->writesize = 512;
+       mtd->writebufsize = mtd->writesize = 512;
        mtd->oobsize = 16;
+       mtd->ecc_strength = 2;
        mtd->owner = THIS_MODULE;
-       mtd->erase = doc_erase;
-       mtd->read = doc_read;
-       mtd->write = doc_write;
-       mtd->read_oob = doc_read_oob;
-       mtd->write_oob = doc_write_oob;
+       mtd->_erase = doc_erase;
+       mtd->_read = doc_read;
+       mtd->_write = doc_write;
+       mtd->_read_oob = doc_read_oob;
+       mtd->_write_oob = doc_write_oob;
        this->curfloor = -1;
        this->curchip = -1;
 
@@ -383,10 +384,6 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
        void __iomem *docptr = this->virtadr;
        struct Nand *mychip = &this->chips[from >> (this->chipshift)];
 
-       /* Don't allow read past end of device */
-       if (from >= this->totlen)
-               return -EINVAL;
-
        /* Don't allow a single read to cross a 512-byte block boundary */
        if (from + len > ((from | 0x1ff) + 1))
                len = ((from | 0x1ff) + 1) - from;
@@ -494,10 +491,6 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
        void __iomem *docptr = this->virtadr;
        struct Nand *mychip = &this->chips[to >> (this->chipshift)];
 
-       /* Don't allow write past end of device */
-       if (to >= this->totlen)
-               return -EINVAL;
-
 #if 0
        /* Don't allow a single write to cross a 512-byte block boundary */
        if (to + len > ( (to | 0x1ff) + 1))
@@ -599,7 +592,6 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
                printk("Error programming flash\n");
                /* Error in programming
                   FIXME: implement Bad Block Replacement (in nftl.c ??) */
-               *retlen = 0;
                ret = -EIO;
        }
        dummy = ReadDOC(docptr, LastDataRead);
index 177510d0e7ee0c35a23450e6f66b7fdc944c5a6c..04eb2e4aa50f1bdfd0aaae18de0b8700aef00483 100644 (file)
@@ -467,14 +467,15 @@ void DoCMilPlus_init(struct mtd_info *mtd)
 
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->writesize = 512;
+       mtd->writebufsize = mtd->writesize = 512;
        mtd->oobsize = 16;
+       mtd->ecc_strength = 2;
        mtd->owner = THIS_MODULE;
-       mtd->erase = doc_erase;
-       mtd->read = doc_read;
-       mtd->write = doc_write;
-       mtd->read_oob = doc_read_oob;
-       mtd->write_oob = doc_write_oob;
+       mtd->_erase = doc_erase;
+       mtd->_read = doc_read;
+       mtd->_write = doc_write;
+       mtd->_read_oob = doc_read_oob;
+       mtd->_write_oob = doc_write_oob;
        this->curfloor = -1;
        this->curchip = -1;
 
@@ -581,10 +582,6 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
        void __iomem * docptr = this->virtadr;
        struct Nand *mychip = &this->chips[from >> (this->chipshift)];
 
-       /* Don't allow read past end of device */
-       if (from >= this->totlen)
-               return -EINVAL;
-
        /* Don't allow a single read to cross a 512-byte block boundary */
        if (from + len > ((from | 0x1ff) + 1))
                len = ((from | 0x1ff) + 1) - from;
@@ -700,10 +697,6 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
        void __iomem * docptr = this->virtadr;
        struct Nand *mychip = &this->chips[to >> (this->chipshift)];
 
-       /* Don't allow write past end of device */
-       if (to >= this->totlen)
-               return -EINVAL;
-
        /* Don't allow writes which aren't exactly one block (512 bytes) */
        if ((to & 0x1ff) || (len != 0x200))
                return -EINVAL;
@@ -800,7 +793,6 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
                printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to);
                /* Error in programming
                   FIXME: implement Bad Block Replacement (in nftl.c ??) */
-               *retlen = 0;
                ret = -EIO;
        }
        dummy = ReadDOC(docptr, Mplus_LastDataRead);
index ad11ef0a81f401dc0f2b4e2440f5d27af10e4708..8272c02668d6ebccf54ad75b71da07158fd617be 100644 (file)
@@ -80,14 +80,9 @@ static struct nand_ecclayout docg3_oobinfo = {
        .oobavail = 8,
 };
 
-/**
- * struct docg3_bch - BCH engine
- */
-static struct bch_control *docg3_bch;
-
 static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
 {
-       u8 val = readb(docg3->base + reg);
+       u8 val = readb(docg3->cascade->base + reg);
 
        trace_docg3_io(0, 8, reg, (int)val);
        return val;
@@ -95,7 +90,7 @@ static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
 
 static inline u16 doc_readw(struct docg3 *docg3, u16 reg)
 {
-       u16 val = readw(docg3->base + reg);
+       u16 val = readw(docg3->cascade->base + reg);
 
        trace_docg3_io(0, 16, reg, (int)val);
        return val;
@@ -103,13 +98,13 @@ static inline u16 doc_readw(struct docg3 *docg3, u16 reg)
 
 static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg)
 {
-       writeb(val, docg3->base + reg);
+       writeb(val, docg3->cascade->base + reg);
        trace_docg3_io(1, 8, reg, val);
 }
 
 static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg)
 {
-       writew(val, docg3->base + reg);
+       writew(val, docg3->cascade->base + reg);
        trace_docg3_io(1, 16, reg, val);
 }
 
@@ -643,7 +638,8 @@ static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc)
 
        for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
                ecc[i] = bitrev8(hwecc[i]);
-       numerrs = decode_bch(docg3_bch, NULL, DOC_ECC_BCH_COVERED_BYTES,
+       numerrs = decode_bch(docg3->cascade->bch, NULL,
+                            DOC_ECC_BCH_COVERED_BYTES,
                             NULL, ecc, NULL, errorpos);
        BUG_ON(numerrs == -EINVAL);
        if (numerrs < 0)
@@ -734,7 +730,7 @@ err:
  * doc_read_page_getbytes - Reads bytes from a prepared page
  * @docg3: the device
  * @len: the number of bytes to be read (must be a multiple of 4)
- * @buf: the buffer to be filled in
+ * @buf: the buffer to be filled in (or NULL is forget bytes)
  * @first: 1 if first time read, DOC_READADDRESS should be set
  *
  */
@@ -849,7 +845,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
                        struct mtd_oob_ops *ops)
 {
        struct docg3 *docg3 = mtd->priv;
-       int block0, block1, page, ret, ofs = 0;
+       int block0, block1, page, ret, skip, ofs = 0;
        u8 *oobbuf = ops->oobbuf;
        u8 *buf = ops->datbuf;
        size_t len, ooblen, nbdata, nboob;
@@ -869,34 +865,36 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
 
        doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n",
                from, ops->mode, buf, len, oobbuf, ooblen);
-       if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % DOC_LAYOUT_OOB_SIZE) ||
-           (from % DOC_LAYOUT_PAGE_SIZE))
+       if (ooblen % DOC_LAYOUT_OOB_SIZE)
                return -EINVAL;
 
-       ret = -EINVAL;
-       calc_block_sector(from + len, &block0, &block1, &page, &ofs,
-                         docg3->reliable);
-       if (block1 > docg3->max_block)
-               goto err;
+       if (from + len > mtd->size)
+               return -EINVAL;
 
        ops->oobretlen = 0;
        ops->retlen = 0;
        ret = 0;
+       skip = from % DOC_LAYOUT_PAGE_SIZE;
+       mutex_lock(&docg3->cascade->lock);
        while (!ret && (len > 0 || ooblen > 0)) {
-               calc_block_sector(from, &block0, &block1, &page, &ofs,
+               calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
                        docg3->reliable);
-               nbdata = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE);
+               nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip);
                nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE);
                ret = doc_read_page_prepare(docg3, block0, block1, page, ofs);
                if (ret < 0)
-                       goto err;
+                       goto out;
                ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
                if (ret < 0)
                        goto err_in_read;
-               ret = doc_read_page_getbytes(docg3, nbdata, buf, 1);
+               ret = doc_read_page_getbytes(docg3, skip, NULL, 1);
+               if (ret < skip)
+                       goto err_in_read;
+               ret = doc_read_page_getbytes(docg3, nbdata, buf, 0);
                if (ret < nbdata)
                        goto err_in_read;
-               doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE - nbdata,
+               doc_read_page_getbytes(docg3,
+                                      DOC_LAYOUT_PAGE_SIZE - nbdata - skip,
                                       NULL, 0);
                ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0);
                if (ret < nboob)
@@ -950,13 +948,15 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
                len -= nbdata;
                ooblen -= nboob;
                from += DOC_LAYOUT_PAGE_SIZE;
+               skip = 0;
        }
 
+out:
+       mutex_unlock(&docg3->cascade->lock);
        return ret;
 err_in_read:
        doc_read_page_finish(docg3);
-err:
-       return ret;
+       goto out;
 }
 
 /**
@@ -1114,10 +1114,10 @@ static int doc_get_op_status(struct docg3 *docg3)
  */
 static int doc_write_erase_wait_status(struct docg3 *docg3)
 {
-       int status, ret = 0;
+       int i, status, ret = 0;
 
-       if (!doc_is_ready(docg3))
-               usleep_range(3000, 3000);
+       for (i = 0; !doc_is_ready(docg3) && i < 5; i++)
+               msleep(20);
        if (!doc_is_ready(docg3)) {
                doc_dbg("Timeout reached and the chip is still not ready\n");
                ret = -EAGAIN;
@@ -1196,18 +1196,19 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
        int block0, block1, page, ret, ofs = 0;
 
        doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len);
-       doc_set_device_id(docg3, docg3->device_id);
 
        info->state = MTD_ERASE_PENDING;
        calc_block_sector(info->addr + info->len, &block0, &block1, &page,
                          &ofs, docg3->reliable);
        ret = -EINVAL;
-       if (block1 > docg3->max_block || page || ofs)
+       if (info->addr + info->len > mtd->size || page || ofs)
                goto reset_err;
 
        ret = 0;
        calc_block_sector(info->addr, &block0, &block1, &page, &ofs,
                          docg3->reliable);
+       mutex_lock(&docg3->cascade->lock);
+       doc_set_device_id(docg3, docg3->device_id);
        doc_set_reliable_mode(docg3);
        for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
                info->state = MTD_ERASING;
@@ -1215,6 +1216,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
                block0 += 2;
                block1 += 2;
        }
+       mutex_unlock(&docg3->cascade->lock);
 
        if (ret)
                goto reset_err;
@@ -1401,7 +1403,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
                         struct mtd_oob_ops *ops)
 {
        struct docg3 *docg3 = mtd->priv;
-       int block0, block1, page, ret, pofs = 0, autoecc, oobdelta;
+       int ret, autoecc, oobdelta;
        u8 *oobbuf = ops->oobbuf;
        u8 *buf = ops->datbuf;
        size_t len, ooblen;
@@ -1438,12 +1440,8 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
        if (len && ooblen &&
            (len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta))
                return -EINVAL;
-
-       ret = -EINVAL;
-       calc_block_sector(ofs + len, &block0, &block1, &page, &pofs,
-                         docg3->reliable);
-       if (block1 > docg3->max_block)
-               goto err;
+       if (ofs + len > mtd->size)
+               return -EINVAL;
 
        ops->oobretlen = 0;
        ops->retlen = 0;
@@ -1457,6 +1455,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
        if (autoecc < 0)
                return autoecc;
 
+       mutex_lock(&docg3->cascade->lock);
        while (!ret && len > 0) {
                memset(oob, 0, sizeof(oob));
                if (ofs == docg3->oob_write_ofs)
@@ -1477,8 +1476,9 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
                }
                ops->retlen += DOC_LAYOUT_PAGE_SIZE;
        }
-err:
+
        doc_set_device_id(docg3, 0);
+       mutex_unlock(&docg3->cascade->lock);
        return ret;
 }
 
@@ -1535,9 +1535,11 @@ static ssize_t dps0_is_key_locked(struct device *dev,
        struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
        int dps0;
 
+       mutex_lock(&docg3->cascade->lock);
        doc_set_device_id(docg3, docg3->device_id);
        dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
        doc_set_device_id(docg3, 0);
+       mutex_unlock(&docg3->cascade->lock);
 
        return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK));
 }
@@ -1548,9 +1550,11 @@ static ssize_t dps1_is_key_locked(struct device *dev,
        struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
        int dps1;
 
+       mutex_lock(&docg3->cascade->lock);
        doc_set_device_id(docg3, docg3->device_id);
        dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
        doc_set_device_id(docg3, 0);
+       mutex_unlock(&docg3->cascade->lock);
 
        return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK));
 }
@@ -1565,10 +1569,12 @@ static ssize_t dps0_insert_key(struct device *dev,
        if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
                return -EINVAL;
 
+       mutex_lock(&docg3->cascade->lock);
        doc_set_device_id(docg3, docg3->device_id);
        for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
                doc_writeb(docg3, buf[i], DOC_DPS0_KEY);
        doc_set_device_id(docg3, 0);
+       mutex_unlock(&docg3->cascade->lock);
        return count;
 }
 
@@ -1582,10 +1588,12 @@ static ssize_t dps1_insert_key(struct device *dev,
        if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
                return -EINVAL;
 
+       mutex_lock(&docg3->cascade->lock);
        doc_set_device_id(docg3, docg3->device_id);
        for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
                doc_writeb(docg3, buf[i], DOC_DPS1_KEY);
        doc_set_device_id(docg3, 0);
+       mutex_unlock(&docg3->cascade->lock);
        return count;
 }
 
@@ -1601,13 +1609,13 @@ static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = {
 };
 
 static int doc_register_sysfs(struct platform_device *pdev,
-                             struct mtd_info **floors)
+                             struct docg3_cascade *cascade)
 {
        int ret = 0, floor, i = 0;
        struct device *dev = &pdev->dev;
 
-       for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS && floors[floor];
-            floor++)
+       for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS &&
+                    cascade->floors[floor]; floor++)
                for (i = 0; !ret && i < 4; i++)
                        ret = device_create_file(dev, &doc_sys_attrs[floor][i]);
        if (!ret)
@@ -1621,12 +1629,12 @@ static int doc_register_sysfs(struct platform_device *pdev,
 }
 
 static void doc_unregister_sysfs(struct platform_device *pdev,
-                                struct mtd_info **floors)
+                                struct docg3_cascade *cascade)
 {
        struct device *dev = &pdev->dev;
        int floor, i;
 
-       for (floor = 0; floor < DOC_MAX_NBFLOORS && floors[floor];
+       for (floor = 0; floor < DOC_MAX_NBFLOORS && cascade->floors[floor];
             floor++)
                for (i = 0; i < 4; i++)
                        device_remove_file(dev, &doc_sys_attrs[floor][i]);
@@ -1640,7 +1648,11 @@ static int dbg_flashctrl_show(struct seq_file *s, void *p)
        struct docg3 *docg3 = (struct docg3 *)s->private;
 
        int pos = 0;
-       u8 fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+       u8 fctrl;
+
+       mutex_lock(&docg3->cascade->lock);
+       fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+       mutex_unlock(&docg3->cascade->lock);
 
        pos += seq_printf(s,
                 "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n",
@@ -1658,9 +1670,12 @@ static int dbg_asicmode_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
 
-       int pos = 0;
-       int pctrl = doc_register_readb(docg3, DOC_ASICMODE);
-       int mode = pctrl & 0x03;
+       int pos = 0, pctrl, mode;
+
+       mutex_lock(&docg3->cascade->lock);
+       pctrl = doc_register_readb(docg3, DOC_ASICMODE);
+       mode = pctrl & 0x03;
+       mutex_unlock(&docg3->cascade->lock);
 
        pos += seq_printf(s,
                         "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (",
@@ -1692,7 +1707,11 @@ static int dbg_device_id_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
        int pos = 0;
-       int id = doc_register_readb(docg3, DOC_DEVICESELECT);
+       int id;
+
+       mutex_lock(&docg3->cascade->lock);
+       id = doc_register_readb(docg3, DOC_DEVICESELECT);
+       mutex_unlock(&docg3->cascade->lock);
 
        pos += seq_printf(s, "DeviceId = %d\n", id);
        return pos;
@@ -1705,6 +1724,7 @@ static int dbg_protection_show(struct seq_file *s, void *p)
        int pos = 0;
        int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high;
 
+       mutex_lock(&docg3->cascade->lock);
        protect = doc_register_readb(docg3, DOC_PROTECTION);
        dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
        dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW);
@@ -1712,6 +1732,7 @@ static int dbg_protection_show(struct seq_file *s, void *p)
        dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
        dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW);
        dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH);
+       mutex_unlock(&docg3->cascade->lock);
 
        pos += seq_printf(s, "Protection = 0x%02x (",
                         protect);
@@ -1804,7 +1825,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
 
        switch (chip_id) {
        case DOC_CHIPID_G3:
-               mtd->name = kasprintf(GFP_KERNEL, "DiskOnChip G3 floor %d",
+               mtd->name = kasprintf(GFP_KERNEL, "docg3.%d",
                                      docg3->device_id);
                docg3->max_block = 2047;
                break;
@@ -1817,16 +1838,17 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
        mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES;
        if (docg3->reliable == 2)
                mtd->erasesize /= 2;
-       mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
+       mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
        mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
        mtd->owner = THIS_MODULE;
-       mtd->erase = doc_erase;
-       mtd->read = doc_read;
-       mtd->write = doc_write;
-       mtd->read_oob = doc_read_oob;
-       mtd->write_oob = doc_write_oob;
-       mtd->block_isbad = doc_block_isbad;
+       mtd->_erase = doc_erase;
+       mtd->_read = doc_read;
+       mtd->_write = doc_write;
+       mtd->_read_oob = doc_read_oob;
+       mtd->_write_oob = doc_write_oob;
+       mtd->_block_isbad = doc_block_isbad;
        mtd->ecclayout = &docg3_oobinfo;
+       mtd->ecc_strength = DOC_ECC_BCH_T;
 }
 
 /**
@@ -1834,6 +1856,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
  * @base: the io space where the device is probed
  * @floor: the floor of the probed device
  * @dev: the device
+ * @cascade: the cascade of chips this devices will belong to
  *
  * Checks whether a device at the specified IO range, and floor is available.
  *
@@ -1841,8 +1864,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
  * if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is
  * launched.
  */
-static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
-                                        struct device *dev)
+static struct mtd_info * __init
+doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
 {
        int ret, bbt_nbpages;
        u16 chip_id, chip_id_inv;
@@ -1865,7 +1888,7 @@ static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
 
        docg3->dev = dev;
        docg3->device_id = floor;
-       docg3->base = base;
+       docg3->cascade = cascade;
        doc_set_device_id(docg3, docg3->device_id);
        if (!floor)
                doc_set_asic_mode(docg3, DOC_ASICMODE_RESET);
@@ -1882,7 +1905,7 @@ static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
        switch (chip_id) {
        case DOC_CHIPID_G3:
                doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n",
-                        base, floor);
+                        docg3->cascade->base, floor);
                break;
        default:
                doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
@@ -1927,10 +1950,12 @@ static void doc_release_device(struct mtd_info *mtd)
 static int docg3_resume(struct platform_device *pdev)
 {
        int i;
+       struct docg3_cascade *cascade;
        struct mtd_info **docg3_floors, *mtd;
        struct docg3 *docg3;
 
-       docg3_floors = platform_get_drvdata(pdev);
+       cascade = platform_get_drvdata(pdev);
+       docg3_floors = cascade->floors;
        mtd = docg3_floors[0];
        docg3 = mtd->priv;
 
@@ -1952,11 +1977,13 @@ static int docg3_resume(struct platform_device *pdev)
 static int docg3_suspend(struct platform_device *pdev, pm_message_t state)
 {
        int floor, i;
+       struct docg3_cascade *cascade;
        struct mtd_info **docg3_floors, *mtd;
        struct docg3 *docg3;
        u8 ctrl, pwr_down;
 
-       docg3_floors = platform_get_drvdata(pdev);
+       cascade = platform_get_drvdata(pdev);
+       docg3_floors = cascade->floors;
        for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
                mtd = docg3_floors[floor];
                if (!mtd)
@@ -2006,7 +2033,7 @@ static int __init docg3_probe(struct platform_device *pdev)
        struct resource *ress;
        void __iomem *base;
        int ret, floor, found = 0;
-       struct mtd_info **docg3_floors;
+       struct docg3_cascade *cascade;
 
        ret = -ENXIO;
        ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2017,17 +2044,19 @@ static int __init docg3_probe(struct platform_device *pdev)
        base = ioremap(ress->start, DOC_IOSPACE_SIZE);
 
        ret = -ENOMEM;
-       docg3_floors = kzalloc(sizeof(*docg3_floors) * DOC_MAX_NBFLOORS,
-                              GFP_KERNEL);
-       if (!docg3_floors)
+       cascade = kzalloc(sizeof(*cascade) * DOC_MAX_NBFLOORS,
+                         GFP_KERNEL);
+       if (!cascade)
                goto nomem1;
-       docg3_bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
+       cascade->base = base;
+       mutex_init(&cascade->lock);
+       cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
                             DOC_ECC_BCH_PRIMPOLY);
-       if (!docg3_bch)
+       if (!cascade->bch)
                goto nomem2;
 
        for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
-               mtd = doc_probe_device(base, floor, dev);
+               mtd = doc_probe_device(cascade, floor, dev);
                if (IS_ERR(mtd)) {
                        ret = PTR_ERR(mtd);
                        goto err_probe;
@@ -2038,7 +2067,7 @@ static int __init docg3_probe(struct platform_device *pdev)
                        else
                                continue;
                }
-               docg3_floors[floor] = mtd;
+               cascade->floors[floor] = mtd;
                ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL,
                                                0);
                if (ret)
@@ -2046,26 +2075,26 @@ static int __init docg3_probe(struct platform_device *pdev)
                found++;
        }
 
-       ret = doc_register_sysfs(pdev, docg3_floors);
+       ret = doc_register_sysfs(pdev, cascade);
        if (ret)
                goto err_probe;
        if (!found)
                goto notfound;
 
-       platform_set_drvdata(pdev, docg3_floors);
-       doc_dbg_register(docg3_floors[0]->priv);
+       platform_set_drvdata(pdev, cascade);
+       doc_dbg_register(cascade->floors[0]->priv);
        return 0;
 
 notfound:
        ret = -ENODEV;
        dev_info(dev, "No supported DiskOnChip found\n");
 err_probe:
-       free_bch(docg3_bch);
+       kfree(cascade->bch);
        for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
-               if (docg3_floors[floor])
-                       doc_release_device(docg3_floors[floor]);
+               if (cascade->floors[floor])
+                       doc_release_device(cascade->floors[floor]);
 nomem2:
-       kfree(docg3_floors);
+       kfree(cascade);
 nomem1:
        iounmap(base);
 noress:
@@ -2080,19 +2109,19 @@ noress:
  */
 static int __exit docg3_release(struct platform_device *pdev)
 {
-       struct mtd_info **docg3_floors = platform_get_drvdata(pdev);
-       struct docg3 *docg3 = docg3_floors[0]->priv;
-       void __iomem *base = docg3->base;
+       struct docg3_cascade *cascade = platform_get_drvdata(pdev);
+       struct docg3 *docg3 = cascade->floors[0]->priv;
+       void __iomem *base = cascade->base;
        int floor;
 
-       doc_unregister_sysfs(pdev, docg3_floors);
+       doc_unregister_sysfs(pdev, cascade);
        doc_dbg_unregister(docg3);
        for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
-               if (docg3_floors[floor])
-                       doc_release_device(docg3_floors[floor]);
+               if (cascade->floors[floor])
+                       doc_release_device(cascade->floors[floor]);
 
-       kfree(docg3_floors);
-       free_bch(docg3_bch);
+       free_bch(docg3->cascade->bch);
+       kfree(cascade);
        iounmap(base);
        return 0;
 }
index db0da436b49332dbea30971e44392a5fa9887461..19fb93f96a3a4eca50f4f8b6bb01728b48bb064f 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef _MTD_DOCG3_H
 #define _MTD_DOCG3_H
 
+#include <linux/mtd/mtd.h>
+
 /*
  * Flash memory areas :
  *   - 0x0000 .. 0x07ff : IPL
  */
 #define DOC_LAYOUT_DPS_KEY_LENGTH      8
 
+/**
+ * struct docg3_cascade - Cascade of 1 to 4 docg3 chips
+ * @floors: floors (ie. one physical docg3 chip is one floor)
+ * @base: IO space to access all chips in the cascade
+ * @bch: the BCH correcting control structure
+ * @lock: lock to protect docg3 IO space from concurrent accesses
+ */
+struct docg3_cascade {
+       struct mtd_info *floors[DOC_MAX_NBFLOORS];
+       void __iomem *base;
+       struct bch_control *bch;
+       struct mutex lock;
+};
+
 /**
  * struct docg3 - DiskOnChip driver private data
  * @dev: the device currently under control
- * @base: mapped IO space
+ * @cascade: the cascade this device belongs to
  * @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3)
  * @if_cfg: if true, reads are on 16bits, else reads are on 8bits
 
  */
 struct docg3 {
        struct device *dev;
-       void __iomem *base;
+       struct docg3_cascade *cascade;
        unsigned int device_id:4;
        unsigned int if_cfg:1;
        unsigned int reliable:2;
index 3a11ea628e58a2249bbe1d54939a77d2003690cb..82bd00af5cc3841ba6cd44ef51024b5792ba09b5 100644 (file)
@@ -367,9 +367,6 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
    printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n", __func__, instr->addr, instr->len);
 #endif
 
-   /* sanity checks */
-   if (instr->addr + instr->len > mtd->size) return (-EINVAL);
-
    /*
        * check that both start and end of the requested erase are
        * aligned with the erasesize at the appropriate addresses.
@@ -440,10 +437,6 @@ static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retle
    printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n", __func__, (__u32)from, len);
 #endif
 
-   /* sanity checks */
-   if (!len) return (0);
-   if (from + len > mtd->size) return (-EINVAL);
-
    /* we always read len bytes */
    *retlen = len;
 
@@ -522,11 +515,8 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen
    printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n", __func__, (__u32)to, len);
 #endif
 
-   *retlen = 0;
-
    /* sanity checks */
    if (!len) return (0);
-   if (to + len > mtd->size) return (-EINVAL);
 
    /* first, we write a 0xFF.... padded byte until we reach a dword boundary */
    if (to & (BUSWIDTH - 1))
@@ -630,14 +620,15 @@ static int __init lart_flash_init (void)
    mtd.name = module_name;
    mtd.type = MTD_NORFLASH;
    mtd.writesize = 1;
+   mtd.writebufsize = 4;
    mtd.flags = MTD_CAP_NORFLASH;
    mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;
    mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
    mtd.numeraseregions = ARRAY_SIZE(erase_regions);
    mtd.eraseregions = erase_regions;
-   mtd.erase = flash_erase;
-   mtd.read = flash_read;
-   mtd.write = flash_write;
+   mtd._erase = flash_erase;
+   mtd._read = flash_read;
+   mtd._write = flash_write;
    mtd.owner = THIS_MODULE;
 
 #ifdef LART_DEBUG
index 7c60dddbefc0ba055551559c5f2046b10e37eecd..1924d247c1cb924c478ebd644475b799dd044297 100644 (file)
@@ -288,9 +288,6 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
                        __func__, (long long)instr->addr,
                        (long long)instr->len);
 
-       /* sanity checks */
-       if (instr->addr + instr->len > flash->mtd.size)
-               return -EINVAL;
        div_u64_rem(instr->len, mtd->erasesize, &rem);
        if (rem)
                return -EINVAL;
@@ -349,13 +346,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
        pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
                        __func__, (u32)from, len);
 
-       /* sanity checks */
-       if (!len)
-               return 0;
-
-       if (from + len > flash->mtd.size)
-               return -EINVAL;
-
        spi_message_init(&m);
        memset(t, 0, (sizeof t));
 
@@ -371,9 +361,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
        t[1].len = len;
        spi_message_add_tail(&t[1], &m);
 
-       /* Byte count starts at zero. */
-       *retlen = 0;
-
        mutex_lock(&flash->lock);
 
        /* Wait till previous write/erase is done. */
@@ -417,15 +404,6 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
        pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
                        __func__, (u32)to, len);
 
-       *retlen = 0;
-
-       /* sanity checks */
-       if (!len)
-               return(0);
-
-       if (to + len > flash->mtd.size)
-               return -EINVAL;
-
        spi_message_init(&m);
        memset(t, 0, (sizeof t));
 
@@ -509,15 +487,6 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
        pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
                        __func__, (u32)to, len);
 
-       *retlen = 0;
-
-       /* sanity checks */
-       if (!len)
-               return 0;
-
-       if (to + len > flash->mtd.size)
-               return -EINVAL;
-
        spi_message_init(&m);
        memset(t, 0, (sizeof t));
 
@@ -908,14 +877,14 @@ static int __devinit m25p_probe(struct spi_device *spi)
        flash->mtd.writesize = 1;
        flash->mtd.flags = MTD_CAP_NORFLASH;
        flash->mtd.size = info->sector_size * info->n_sectors;
-       flash->mtd.erase = m25p80_erase;
-       flash->mtd.read = m25p80_read;
+       flash->mtd._erase = m25p80_erase;
+       flash->mtd._read = m25p80_read;
 
        /* sst flash chips use AAI word program */
        if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST)
-               flash->mtd.write = sst_write;
+               flash->mtd._write = sst_write;
        else
-               flash->mtd.write = m25p80_write;
+               flash->mtd._write = m25p80_write;
 
        /* prefer "small sector" erase if possible */
        if (info->flags & SECT_4K) {
@@ -932,6 +901,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
        ppdata.of_node = spi->dev.of_node;
        flash->mtd.dev.parent = &spi->dev;
        flash->page_size = info->page_size;
+       flash->mtd.writebufsize = flash->page_size;
 
        if (info->addr_width)
                flash->addr_width = info->addr_width;
@@ -1004,21 +974,7 @@ static struct spi_driver m25p80_driver = {
         */
 };
 
-
-static int __init m25p80_init(void)
-{
-       return spi_register_driver(&m25p80_driver);
-}
-
-
-static void __exit m25p80_exit(void)
-{
-       spi_unregister_driver(&m25p80_driver);
-}
-
-
-module_init(m25p80_init);
-module_exit(m25p80_exit);
+module_spi_driver(m25p80_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Mike Lavender");
index 8423fb6d4f26b05df635ee14608e654599627fc7..182849d39c61acb34c40a79ab6bf04e30162da79 100644 (file)
@@ -59,12 +59,8 @@ static int ms02nv_read(struct mtd_info *mtd, loff_t from,
 {
        struct ms02nv_private *mp = mtd->priv;
 
-       if (from + len > mtd->size)
-               return -EINVAL;
-
        memcpy(buf, mp->uaddr + from, len);
        *retlen = len;
-
        return 0;
 }
 
@@ -73,12 +69,8 @@ static int ms02nv_write(struct mtd_info *mtd, loff_t to,
 {
        struct ms02nv_private *mp = mtd->priv;
 
-       if (to + len > mtd->size)
-               return -EINVAL;
-
        memcpy(mp->uaddr + to, buf, len);
        *retlen = len;
-
        return 0;
 }
 
@@ -215,8 +207,8 @@ static int __init ms02nv_init_one(ulong addr)
        mtd->size = fixsize;
        mtd->name = (char *)ms02nv_name;
        mtd->owner = THIS_MODULE;
-       mtd->read = ms02nv_read;
-       mtd->write = ms02nv_write;
+       mtd->_read = ms02nv_read;
+       mtd->_write = ms02nv_write;
        mtd->writesize = 1;
 
        ret = -EIO;
index 236057ead0d2ab2fcd5d57bff92e18a05e927223..928fb0e6d73a632c9a5522a03a5e6a07284a4c53 100644 (file)
@@ -164,9 +164,6 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
              dev_name(&spi->dev), (long long)instr->addr,
              (long long)instr->len);
 
-       /* Sanity checks */
-       if (instr->addr + instr->len > mtd->size)
-               return -EINVAL;
        div_u64_rem(instr->len, priv->page_size, &rem);
        if (rem)
                return -EINVAL;
@@ -252,14 +249,6 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
        pr_debug("%s: read 0x%x..0x%x\n", dev_name(&priv->spi->dev),
                        (unsigned)from, (unsigned)(from + len));
 
-       *retlen = 0;
-
-       /* Sanity checks */
-       if (!len)
-               return 0;
-       if (from + len > mtd->size)
-               return -EINVAL;
-
        /* Calculate flash page/byte address */
        addr = (((unsigned)from / priv->page_size) << priv->page_offset)
                + ((unsigned)from % priv->page_size);
@@ -328,14 +317,6 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
        pr_debug("%s: write 0x%x..0x%x\n",
                dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len));
 
-       *retlen = 0;
-
-       /* Sanity checks */
-       if (!len)
-               return 0;
-       if ((to + len) > mtd->size)
-               return -EINVAL;
-
        spi_message_init(&msg);
 
        x[0].tx_buf = command = priv->command;
@@ -490,8 +471,6 @@ static ssize_t otp_read(struct spi_device *spi, unsigned base,
 
        if ((off + len) > 64)
                len = 64 - off;
-       if (len == 0)
-               return len;
 
        spi_message_init(&m);
 
@@ -611,16 +590,16 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
 
 static char *otp_setup(struct mtd_info *device, char revision)
 {
-       device->get_fact_prot_info = dataflash_get_otp_info;
-       device->read_fact_prot_reg = dataflash_read_fact_otp;
-       device->get_user_prot_info = dataflash_get_otp_info;
-       device->read_user_prot_reg = dataflash_read_user_otp;
+       device->_get_fact_prot_info = dataflash_get_otp_info;
+       device->_read_fact_prot_reg = dataflash_read_fact_otp;
+       device->_get_user_prot_info = dataflash_get_otp_info;
+       device->_read_user_prot_reg = dataflash_read_user_otp;
 
        /* rev c parts (at45db321c and at45db1281 only!) use a
         * different write procedure; not (yet?) implemented.
         */
        if (revision > 'c')
-               device->write_user_prot_reg = dataflash_write_user_otp;
+               device->_write_user_prot_reg = dataflash_write_user_otp;
 
        return ", OTP";
 }
@@ -672,9 +651,9 @@ add_dataflash_otp(struct spi_device *spi, char *name,
        device->owner = THIS_MODULE;
        device->type = MTD_DATAFLASH;
        device->flags = MTD_WRITEABLE;
-       device->erase = dataflash_erase;
-       device->read = dataflash_read;
-       device->write = dataflash_write;
+       device->_erase = dataflash_erase;
+       device->_read = dataflash_read;
+       device->_write = dataflash_write;
        device->priv = priv;
 
        device->dev.parent = &spi->dev;
@@ -946,18 +925,7 @@ static struct spi_driver dataflash_driver = {
        /* FIXME:  investigate suspend and resume... */
 };
 
-static int __init dataflash_init(void)
-{
-       return spi_register_driver(&dataflash_driver);
-}
-module_init(dataflash_init);
-
-static void __exit dataflash_exit(void)
-{
-       spi_unregister_driver(&dataflash_driver);
-}
-module_exit(dataflash_exit);
-
+module_spi_driver(dataflash_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Andrew Victor, David Brownell");
index 2562689ba6b47d185edd5c3ba862af50a8ae97a8..ec59d65897fbe38976112ab071741b6c2d3b9189 100644 (file)
@@ -34,34 +34,23 @@ static struct mtd_info *mtd_info;
 
 static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
-       if (instr->addr + instr->len > mtd->size)
-               return -EINVAL;
-
        memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
-
        instr->state = MTD_ERASE_DONE;
        mtd_erase_callback(instr);
-
        return 0;
 }
 
 static int ram_point(struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, void **virt, resource_size_t *phys)
 {
-       if (from + len > mtd->size)
-               return -EINVAL;
-
-       /* can we return a physical address with this driver? */
-       if (phys)
-               return -EINVAL;
-
        *virt = mtd->priv + from;
        *retlen = len;
        return 0;
 }
 
-static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 {
+       return 0;
 }
 
 /*
@@ -80,11 +69,7 @@ static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,
 static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, u_char *buf)
 {
-       if (from + len > mtd->size)
-               return -EINVAL;
-
        memcpy(buf, mtd->priv + from, len);
-
        *retlen = len;
        return 0;
 }
@@ -92,11 +77,7 @@ static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
 static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf)
 {
-       if (to + len > mtd->size)
-               return -EINVAL;
-
        memcpy((char *)mtd->priv + to, buf, len);
-
        *retlen = len;
        return 0;
 }
@@ -126,12 +107,12 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
        mtd->priv = mapped_address;
 
        mtd->owner = THIS_MODULE;
-       mtd->erase = ram_erase;
-       mtd->point = ram_point;
-       mtd->unpoint = ram_unpoint;
-       mtd->get_unmapped_area = ram_get_unmapped_area;
-       mtd->read = ram_read;
-       mtd->write = ram_write;
+       mtd->_erase = ram_erase;
+       mtd->_point = ram_point;
+       mtd->_unpoint = ram_unpoint;
+       mtd->_get_unmapped_area = ram_get_unmapped_area;
+       mtd->_read = ram_read;
+       mtd->_write = ram_write;
 
        if (mtd_device_register(mtd, NULL, 0))
                return -EIO;
index 23423bd00b069da5266d72cbfa6353f21ec775dd..67823de68db69e7f2ae80d378ed7b9289eb811e0 100644 (file)
@@ -33,45 +33,33 @@ struct phram_mtd_list {
 
 static LIST_HEAD(phram_list);
 
-
 static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        u_char *start = mtd->priv;
 
-       if (instr->addr + instr->len > mtd->size)
-               return -EINVAL;
-
        memset(start + instr->addr, 0xff, instr->len);
 
-       /* This'll catch a few races. Free the thing before returning :)
+       /*
+        * This'll catch a few races. Free the thing before returning :)
         * I don't feel at all ashamed. This kind of thing is possible anyway
         * with flash, but unlikely.
         */
-
        instr->state = MTD_ERASE_DONE;
-
        mtd_erase_callback(instr);
-
        return 0;
 }
 
 static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, void **virt, resource_size_t *phys)
 {
-       if (from + len > mtd->size)
-               return -EINVAL;
-
-       /* can we return a physical address with this driver? */
-       if (phys)
-               return -EINVAL;
-
        *virt = mtd->priv + from;
        *retlen = len;
        return 0;
 }
 
-static void phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 {
+       return 0;
 }
 
 static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -79,14 +67,7 @@ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
 {
        u_char *start = mtd->priv;
 
-       if (from >= mtd->size)
-               return -EINVAL;
-
-       if (len > mtd->size - from)
-               len = mtd->size - from;
-
        memcpy(buf, start + from, len);
-
        *retlen = len;
        return 0;
 }
@@ -96,20 +77,11 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
 {
        u_char *start = mtd->priv;
 
-       if (to >= mtd->size)
-               return -EINVAL;
-
-       if (len > mtd->size - to)
-               len = mtd->size - to;
-
        memcpy(start + to, buf, len);
-
        *retlen = len;
        return 0;
 }
 
-
-
 static void unregister_devices(void)
 {
        struct phram_mtd_list *this, *safe;
@@ -142,11 +114,11 @@ static int register_device(char *name, unsigned long start, unsigned long len)
        new->mtd.name = name;
        new->mtd.size = len;
        new->mtd.flags = MTD_CAP_RAM;
-        new->mtd.erase = phram_erase;
-       new->mtd.point = phram_point;
-       new->mtd.unpoint = phram_unpoint;
-       new->mtd.read = phram_read;
-       new->mtd.write = phram_write;
+       new->mtd._erase = phram_erase;
+       new->mtd._point = phram_point;
+       new->mtd._unpoint = phram_unpoint;
+       new->mtd._read = phram_read;
+       new->mtd._write = phram_write;
        new->mtd.owner = THIS_MODULE;
        new->mtd.type = MTD_RAM;
        new->mtd.erasesize = PAGE_SIZE;
@@ -233,7 +205,17 @@ static inline void kill_final_newline(char *str)
        return 1;               \
 } while (0)
 
-static int phram_setup(const char *val, struct kernel_param *kp)
+/*
+ * This shall contain the module parameter if any. It is of the form:
+ * - phram=<device>,<address>,<size> for module case
+ * - phram.phram=<device>,<address>,<size> for built-in case
+ * We leave 64 bytes for the device name, 12 for the address and 12 for the
+ * size.
+ * Example: phram.phram=rootfs,0xa0000000,512Mi
+ */
+static __initdata char phram_paramline[64+12+12];
+
+static int __init phram_setup(const char *val)
 {
        char buf[64+12+12], *str = buf;
        char *token[3];
@@ -282,12 +264,28 @@ static int phram_setup(const char *val, struct kernel_param *kp)
        return ret;
 }
 
-module_param_call(phram, phram_setup, NULL, NULL, 000);
+static int __init phram_param_call(const char *val, struct kernel_param *kp)
+{
+       /*
+        * This function is always called before 'init_phram()', whether
+        * built-in or module.
+        */
+       if (strlen(val) >= sizeof(phram_paramline))
+               return -ENOSPC;
+       strcpy(phram_paramline, val);
+
+       return 0;
+}
+
+module_param_call(phram, phram_param_call, NULL, NULL, 000);
 MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"");
 
 
 static int __init init_phram(void)
 {
+       if (phram_paramline[0])
+               return phram_setup(phram_paramline);
+
        return 0;
 }
 
index 5d53c5760a6cfa71adc20fe92d862497acb33aff..0c51b988e1f8880af884995540cee3c5ac91dfca 100644 (file)
 #include <linux/ioctl.h>
 #include <asm/io.h>
 #include <linux/pci.h>
-
 #include <linux/mtd/mtd.h>
-#include <linux/mtd/pmc551.h>
+
+#define PMC551_VERSION \
+       "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n"
+
+#define PCI_VENDOR_ID_V3_SEMI 0x11b0
+#define PCI_DEVICE_ID_V3_SEMI_V370PDC 0x0200
+
+#define PMC551_PCI_MEM_MAP0 0x50
+#define PMC551_PCI_MEM_MAP1 0x54
+#define PMC551_PCI_MEM_MAP_MAP_ADDR_MASK 0x3ff00000
+#define PMC551_PCI_MEM_MAP_APERTURE_MASK 0x000000f0
+#define PMC551_PCI_MEM_MAP_REG_EN 0x00000002
+#define PMC551_PCI_MEM_MAP_ENABLE 0x00000001
+
+#define PMC551_SDRAM_MA  0x60
+#define PMC551_SDRAM_CMD 0x62
+#define PMC551_DRAM_CFG  0x64
+#define PMC551_SYS_CTRL_REG 0x78
+
+#define PMC551_DRAM_BLK0 0x68
+#define PMC551_DRAM_BLK1 0x6c
+#define PMC551_DRAM_BLK2 0x70
+#define PMC551_DRAM_BLK3 0x74
+#define PMC551_DRAM_BLK_GET_SIZE(x) (524288 << ((x >> 4) & 0x0f))
+#define PMC551_DRAM_BLK_SET_COL_MUX(x, v) (((x) & ~0x00007000) | (((v) & 0x7) << 12))
+#define PMC551_DRAM_BLK_SET_ROW_MUX(x, v) (((x) & ~0x00000f00) | (((v) & 0xf) << 8))
+
+struct mypriv {
+       struct pci_dev *dev;
+       u_char *start;
+       u32 base_map0;
+       u32 curr_map0;
+       u32 asize;
+       struct mtd_info *nextpmc551;
+};
 
 static struct mtd_info *pmc551list;
 
+static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
+                       size_t *retlen, void **virt, resource_size_t *phys);
+
 static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct mypriv *priv = mtd->priv;
@@ -115,16 +151,6 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
 #endif
 
        end = instr->addr + instr->len - 1;
-
-       /* Is it past the end? */
-       if (end > mtd->size) {
-#ifdef CONFIG_MTD_PMC551_DEBUG
-               printk(KERN_DEBUG "pmc551_erase() out of bounds (%ld > %ld)\n",
-                       (long)end, (long)mtd->size);
-#endif
-               return -EINVAL;
-       }
-
        eoff_hi = end & ~(priv->asize - 1);
        soff_hi = instr->addr & ~(priv->asize - 1);
        eoff_lo = end & (priv->asize - 1);
@@ -178,18 +204,6 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
        printk(KERN_DEBUG "pmc551_point(%ld, %ld)\n", (long)from, (long)len);
 #endif
 
-       if (from + len > mtd->size) {
-#ifdef CONFIG_MTD_PMC551_DEBUG
-               printk(KERN_DEBUG "pmc551_point() out of bounds (%ld > %ld)\n",
-                       (long)from + len, (long)mtd->size);
-#endif
-               return -EINVAL;
-       }
-
-       /* can we return a physical address with this driver? */
-       if (phys)
-               return -EINVAL;
-
        soff_hi = from & ~(priv->asize - 1);
        soff_lo = from & (priv->asize - 1);
 
@@ -205,11 +219,12 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
        return 0;
 }
 
-static void pmc551_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int pmc551_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 {
 #ifdef CONFIG_MTD_PMC551_DEBUG
        printk(KERN_DEBUG "pmc551_unpoint()\n");
 #endif
+       return 0;
 }
 
 static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -228,16 +243,6 @@ static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,
 #endif
 
        end = from + len - 1;
-
-       /* Is it past the end? */
-       if (end > mtd->size) {
-#ifdef CONFIG_MTD_PMC551_DEBUG
-               printk(KERN_DEBUG "pmc551_read() out of bounds (%ld > %ld)\n",
-                       (long)end, (long)mtd->size);
-#endif
-               return -EINVAL;
-       }
-
        soff_hi = from & ~(priv->asize - 1);
        eoff_hi = end & ~(priv->asize - 1);
        soff_lo = from & (priv->asize - 1);
@@ -295,16 +300,6 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
 #endif
 
        end = to + len - 1;
-       /* Is it past the end?  or did the u32 wrap? */
-       if (end > mtd->size) {
-#ifdef CONFIG_MTD_PMC551_DEBUG
-               printk(KERN_DEBUG "pmc551_write() out of bounds (end: %ld, "
-                       "size: %ld, to: %ld)\n", (long)end, (long)mtd->size,
-                       (long)to);
-#endif
-               return -EINVAL;
-       }
-
        soff_hi = to & ~(priv->asize - 1);
        eoff_hi = end & ~(priv->asize - 1);
        soff_lo = to & (priv->asize - 1);
@@ -358,7 +353,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
  * mechanism
  * returns the size of the memory region found.
  */
-static u32 fixup_pmc551(struct pci_dev *dev)
+static int fixup_pmc551(struct pci_dev *dev)
 {
 #ifdef CONFIG_MTD_PMC551_BUGFIX
        u32 dram_data;
@@ -668,7 +663,7 @@ static int __init init_pmc551(void)
        struct mypriv *priv;
        int found = 0;
        struct mtd_info *mtd;
-       u32 length = 0;
+       int length = 0;
 
        if (msize) {
                msize = (1 << (ffs(msize) - 1)) << 20;
@@ -786,11 +781,11 @@ static int __init init_pmc551(void)
 
                mtd->size = msize;
                mtd->flags = MTD_CAP_RAM;
-               mtd->erase = pmc551_erase;
-               mtd->read = pmc551_read;
-               mtd->write = pmc551_write;
-               mtd->point = pmc551_point;
-               mtd->unpoint = pmc551_unpoint;
+               mtd->_erase = pmc551_erase;
+               mtd->_read = pmc551_read;
+               mtd->_write = pmc551_write;
+               mtd->_point = pmc551_point;
+               mtd->_unpoint = pmc551_unpoint;
                mtd->type = MTD_RAM;
                mtd->name = "PMC551 RAM board";
                mtd->erasesize = 0x10000;
index 288594163c22da60e4c932ea16e78c3b9bf1f6bf..8f52fc858e48bec08f900963996c0d37bd4601cb 100644 (file)
@@ -75,7 +75,7 @@ static slram_mtd_list_t *slram_mtdlist = NULL;
 static int slram_erase(struct mtd_info *, struct erase_info *);
 static int slram_point(struct mtd_info *, loff_t, size_t, size_t *, void **,
                resource_size_t *);
-static void slram_unpoint(struct mtd_info *, loff_t, size_t);
+static int slram_unpoint(struct mtd_info *, loff_t, size_t);
 static int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
 
@@ -83,21 +83,13 @@ static int slram_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        slram_priv_t *priv = mtd->priv;
 
-       if (instr->addr + instr->len > mtd->size) {
-               return(-EINVAL);
-       }
-
        memset(priv->start + instr->addr, 0xff, instr->len);
-
        /* This'll catch a few races. Free the thing before returning :)
         * I don't feel at all ashamed. This kind of thing is possible anyway
         * with flash, but unlikely.
         */
-
        instr->state = MTD_ERASE_DONE;
-
        mtd_erase_callback(instr);
-
        return(0);
 }
 
@@ -106,20 +98,14 @@ static int slram_point(struct mtd_info *mtd, loff_t from, size_t len,
 {
        slram_priv_t *priv = mtd->priv;
 
-       /* can we return a physical address with this driver? */
-       if (phys)
-               return -EINVAL;
-
-       if (from + len > mtd->size)
-               return -EINVAL;
-
        *virt = priv->start + from;
        *retlen = len;
        return(0);
 }
 
-static void slram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int slram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 {
+       return 0;
 }
 
 static int slram_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -127,14 +113,7 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len,
 {
        slram_priv_t *priv = mtd->priv;
 
-       if (from > mtd->size)
-               return -EINVAL;
-
-       if (from + len > mtd->size)
-               len = mtd->size - from;
-
        memcpy(buf, priv->start + from, len);
-
        *retlen = len;
        return(0);
 }
@@ -144,11 +123,7 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len,
 {
        slram_priv_t *priv = mtd->priv;
 
-       if (to + len > mtd->size)
-               return -EINVAL;
-
        memcpy(priv->start + to, buf, len);
-
        *retlen = len;
        return(0);
 }
@@ -199,11 +174,11 @@ static int register_device(char *name, unsigned long start, unsigned long length
        (*curmtd)->mtdinfo->name = name;
        (*curmtd)->mtdinfo->size = length;
        (*curmtd)->mtdinfo->flags = MTD_CAP_RAM;
-        (*curmtd)->mtdinfo->erase = slram_erase;
-       (*curmtd)->mtdinfo->point = slram_point;
-       (*curmtd)->mtdinfo->unpoint = slram_unpoint;
-       (*curmtd)->mtdinfo->read = slram_read;
-       (*curmtd)->mtdinfo->write = slram_write;
+       (*curmtd)->mtdinfo->_erase = slram_erase;
+       (*curmtd)->mtdinfo->_point = slram_point;
+       (*curmtd)->mtdinfo->_unpoint = slram_unpoint;
+       (*curmtd)->mtdinfo->_read = slram_read;
+       (*curmtd)->mtdinfo->_write = slram_write;
        (*curmtd)->mtdinfo->owner = THIS_MODULE;
        (*curmtd)->mtdinfo->type = MTD_RAM;
        (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ;
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
new file mode 100644 (file)
index 0000000..797d43c
--- /dev/null
@@ -0,0 +1,1147 @@
+/*
+ * SMI (Serial Memory Controller) device driver for Serial NOR Flash on
+ * SPEAr platform
+ * The serial nor interface is largely based on drivers/mtd/m25p80.c,
+ * however the SPI interface has been replaced by SMI.
+ *
+ * Copyright Â© 2010 STMicroelectronics.
+ * Ashish Priyadarshi
+ * Shiraz Hashim <shiraz.hashim@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spear_smi.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+/* SMI clock rate */
+#define SMI_MAX_CLOCK_FREQ     50000000 /* 50 MHz */
+
+/* MAX time out to safely come out of a erase or write busy conditions */
+#define SMI_PROBE_TIMEOUT      (HZ / 10)
+#define SMI_MAX_TIME_OUT       (3 * HZ)
+
+/* timeout for command completion */
+#define SMI_CMD_TIMEOUT                (HZ / 10)
+
+/* registers of smi */
+#define SMI_CR1                0x0     /* SMI control register 1 */
+#define SMI_CR2                0x4     /* SMI control register 2 */
+#define SMI_SR         0x8     /* SMI status register */
+#define SMI_TR         0xC     /* SMI transmit register */
+#define SMI_RR         0x10    /* SMI receive register */
+
+/* defines for control_reg 1 */
+#define BANK_EN                (0xF << 0)      /* enables all banks */
+#define DSEL_TIME      (0x6 << 4)      /* Deselect time 6 + 1 SMI_CK periods */
+#define SW_MODE                (0x1 << 28)     /* enables SW Mode */
+#define WB_MODE                (0x1 << 29)     /* Write Burst Mode */
+#define FAST_MODE      (0x1 << 15)     /* Fast Mode */
+#define HOLD1          (0x1 << 16)     /* Clock Hold period selection */
+
+/* defines for control_reg 2 */
+#define SEND           (0x1 << 7)      /* Send data */
+#define TFIE           (0x1 << 8)      /* Transmission Flag Interrupt Enable */
+#define WCIE           (0x1 << 9)      /* Write Complete Interrupt Enable */
+#define RD_STATUS_REG  (0x1 << 10)     /* reads status reg */
+#define WE             (0x1 << 11)     /* Write Enable */
+
+#define TX_LEN_SHIFT   0
+#define RX_LEN_SHIFT   4
+#define BANK_SHIFT     12
+
+/* defines for status register */
+#define SR_WIP         0x1     /* Write in progress */
+#define SR_WEL         0x2     /* Write enable latch */
+#define SR_BP0         0x4     /* Block protect 0 */
+#define SR_BP1         0x8     /* Block protect 1 */
+#define SR_BP2         0x10    /* Block protect 2 */
+#define SR_SRWD                0x80    /* SR write protect */
+#define TFF            0x100   /* Transfer Finished Flag */
+#define WCF            0x200   /* Transfer Finished Flag */
+#define ERF1           0x400   /* Forbidden Write Request */
+#define ERF2           0x800   /* Forbidden Access */
+
+#define WM_SHIFT       12
+
+/* flash opcodes */
+#define OPCODE_RDID    0x9f    /* Read JEDEC ID */
+
+/* Flash Device Ids maintenance section */
+
+/* data structure to maintain flash ids from different vendors */
+struct flash_device {
+       char *name;
+       u8 erase_cmd;
+       u32 device_id;
+       u32 pagesize;
+       unsigned long sectorsize;
+       unsigned long size_in_bytes;
+};
+
+#define FLASH_ID(n, es, id, psize, ssize, size)        \
+{                              \
+       .name = n,              \
+       .erase_cmd = es,        \
+       .device_id = id,        \
+       .pagesize = psize,      \
+       .sectorsize = ssize,    \
+       .size_in_bytes = size   \
+}
+
+static struct flash_device flash_devices[] = {
+       FLASH_ID("st m25p16"     , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000),
+       FLASH_ID("st m25p32"     , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000),
+       FLASH_ID("st m25p64"     , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000),
+       FLASH_ID("st m25p128"    , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000),
+       FLASH_ID("st m25p05"     , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000),
+       FLASH_ID("st m25p10"     , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000),
+       FLASH_ID("st m25p20"     , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000),
+       FLASH_ID("st m25p40"     , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000),
+       FLASH_ID("st m25p80"     , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000),
+       FLASH_ID("st m45pe10"    , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000),
+       FLASH_ID("st m45pe20"    , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000),
+       FLASH_ID("st m45pe40"    , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000),
+       FLASH_ID("st m45pe80"    , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000),
+       FLASH_ID("sp s25fl004"   , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000),
+       FLASH_ID("sp s25fl008"   , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000),
+       FLASH_ID("sp s25fl016"   , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000),
+       FLASH_ID("sp s25fl032"   , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000),
+       FLASH_ID("sp s25fl064"   , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000),
+       FLASH_ID("atmel 25f512"  , 0x52, 0x0065001F, 0x80 , 0x8000 , 0x10000),
+       FLASH_ID("atmel 25f1024" , 0x52, 0x0060001F, 0x100, 0x8000 , 0x20000),
+       FLASH_ID("atmel 25f2048" , 0x52, 0x0063001F, 0x100, 0x10000, 0x40000),
+       FLASH_ID("atmel 25f4096" , 0x52, 0x0064001F, 0x100, 0x10000, 0x80000),
+       FLASH_ID("atmel 25fs040" , 0xd7, 0x0004661F, 0x100, 0x10000, 0x80000),
+       FLASH_ID("mac 25l512"    , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000),
+       FLASH_ID("mac 25l1005"   , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000),
+       FLASH_ID("mac 25l2005"   , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000),
+       FLASH_ID("mac 25l4005"   , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000),
+       FLASH_ID("mac 25l4005a"  , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000),
+       FLASH_ID("mac 25l8005"   , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000),
+       FLASH_ID("mac 25l1605"   , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000),
+       FLASH_ID("mac 25l1605a"  , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000),
+       FLASH_ID("mac 25l3205"   , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000),
+       FLASH_ID("mac 25l3205a"  , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000),
+       FLASH_ID("mac 25l6405"   , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000),
+};
+
+/* Define spear specific structures */
+
+struct spear_snor_flash;
+
+/**
+ * struct spear_smi - Structure for SMI Device
+ *
+ * @clk: functional clock
+ * @status: current status register of SMI.
+ * @clk_rate: functional clock rate of SMI (default: SMI_MAX_CLOCK_FREQ)
+ * @lock: lock to prevent parallel access of SMI.
+ * @io_base: base address for registers of SMI.
+ * @pdev: platform device
+ * @cmd_complete: queue to wait for command completion of NOR-flash.
+ * @num_flashes: number of flashes actually present on board.
+ * @flash: separate structure for each Serial NOR-flash attached to SMI.
+ */
+struct spear_smi {
+       struct clk *clk;
+       u32 status;
+       unsigned long clk_rate;
+       struct mutex lock;
+       void __iomem *io_base;
+       struct platform_device *pdev;
+       wait_queue_head_t cmd_complete;
+       u32 num_flashes;
+       struct spear_snor_flash *flash[MAX_NUM_FLASH_CHIP];
+};
+
+/**
+ * struct spear_snor_flash - Structure for Serial NOR Flash
+ *
+ * @bank: Bank number(0, 1, 2, 3) for each NOR-flash.
+ * @dev_id: Device ID of NOR-flash.
+ * @lock: lock to manage flash read, write and erase operations
+ * @mtd: MTD info for each NOR-flash.
+ * @num_parts: Total number of partition in each bank of NOR-flash.
+ * @parts: Partition info for each bank of NOR-flash.
+ * @page_size: Page size of NOR-flash.
+ * @base_addr: Base address of NOR-flash.
+ * @erase_cmd: erase command may vary on different flash types
+ * @fast_mode: flash supports read in fast mode
+ */
+struct spear_snor_flash {
+       u32 bank;
+       u32 dev_id;
+       struct mutex lock;
+       struct mtd_info mtd;
+       u32 num_parts;
+       struct mtd_partition *parts;
+       u32 page_size;
+       void __iomem *base_addr;
+       u8 erase_cmd;
+       u8 fast_mode;
+};
+
+static inline struct spear_snor_flash *get_flash_data(struct mtd_info *mtd)
+{
+       return container_of(mtd, struct spear_snor_flash, mtd);
+}
+
+/**
+ * spear_smi_read_sr - Read status register of flash through SMI
+ * @dev: structure of SMI information.
+ * @bank: bank to which flash is connected
+ *
+ * This routine will return the status register of the flash chip present at the
+ * given bank.
+ */
+static int spear_smi_read_sr(struct spear_smi *dev, u32 bank)
+{
+       int ret;
+       u32 ctrlreg1;
+
+       mutex_lock(&dev->lock);
+       dev->status = 0; /* Will be set in interrupt handler */
+
+       ctrlreg1 = readl(dev->io_base + SMI_CR1);
+       /* program smi in hw mode */
+       writel(ctrlreg1 & ~(SW_MODE | WB_MODE), dev->io_base + SMI_CR1);
+
+       /* performing a rsr instruction in hw mode */
+       writel((bank << BANK_SHIFT) | RD_STATUS_REG | TFIE,
+                       dev->io_base + SMI_CR2);
+
+       /* wait for tff */
+       ret = wait_event_interruptible_timeout(dev->cmd_complete,
+                       dev->status & TFF, SMI_CMD_TIMEOUT);
+
+       /* copy dev->status (lower 16 bits) in order to release lock */
+       if (ret > 0)
+               ret = dev->status & 0xffff;
+       else
+               ret = -EIO;
+
+       /* restore the ctrl regs state */
+       writel(ctrlreg1, dev->io_base + SMI_CR1);
+       writel(0, dev->io_base + SMI_CR2);
+       mutex_unlock(&dev->lock);
+
+       return ret;
+}
+
+/**
+ * spear_smi_wait_till_ready - wait till flash is ready
+ * @dev: structure of SMI information.
+ * @bank: flash corresponding to this bank
+ * @timeout: timeout for busy wait condition
+ *
+ * This routine checks for WIP (write in progress) bit in Status register
+ * If successful the routine returns 0 else -EBUSY
+ */
+static int spear_smi_wait_till_ready(struct spear_smi *dev, u32 bank,
+               unsigned long timeout)
+{
+       unsigned long finish;
+       int status;
+
+       finish = jiffies + timeout;
+       do {
+               status = spear_smi_read_sr(dev, bank);
+               if (status < 0)
+                       continue; /* try till timeout */
+               else if (!(status & SR_WIP))
+                       return 0;
+
+               cond_resched();
+       } while (!time_after_eq(jiffies, finish));
+
+       dev_err(&dev->pdev->dev, "smi controller is busy, timeout\n");
+       return status;
+}
+
+/**
+ * spear_smi_int_handler - SMI Interrupt Handler.
+ * @irq: irq number
+ * @dev_id: structure of SMI device, embedded in dev_id.
+ *
+ * The handler clears all interrupt conditions and records the status in
+ * dev->status which is used by the driver later.
+ */
+static irqreturn_t spear_smi_int_handler(int irq, void *dev_id)
+{
+       u32 status = 0;
+       struct spear_smi *dev = dev_id;
+
+       status = readl(dev->io_base + SMI_SR);
+
+       if (unlikely(!status))
+               return IRQ_NONE;
+
+       /* clear all interrupt conditions */
+       writel(0, dev->io_base + SMI_SR);
+
+       /* copy the status register in dev->status */
+       dev->status |= status;
+
+       /* send the completion */
+       wake_up_interruptible(&dev->cmd_complete);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * spear_smi_hw_init - initializes the smi controller.
+ * @dev: structure of smi device
+ *
+ * this routine initializes the smi controller wit the default values
+ */
+static void spear_smi_hw_init(struct spear_smi *dev)
+{
+       unsigned long rate = 0;
+       u32 prescale = 0;
+       u32 val;
+
+       rate = clk_get_rate(dev->clk);
+
+       /* functional clock of smi */
+       prescale = DIV_ROUND_UP(rate, dev->clk_rate);
+
+       /*
+        * setting the standard values, fast mode, prescaler for
+        * SMI_MAX_CLOCK_FREQ (50MHz) operation and bank enable
+        */
+       val = HOLD1 | BANK_EN | DSEL_TIME | (prescale << 8);
+
+       mutex_lock(&dev->lock);
+       writel(val, dev->io_base + SMI_CR1);
+       mutex_unlock(&dev->lock);
+}
+
+/**
+ * get_flash_index - match chip id from a flash list.
+ * @flash_id: a valid nor flash chip id obtained from board.
+ *
+ * try to validate the chip id by matching from a list, if not found then simply
+ * returns negative. In case of success returns index in to the flash devices
+ * array.
+ */
+static int get_flash_index(u32 flash_id)
+{
+       int index;
+
+       /* Matches chip-id to entire list of 'serial-nor flash' ids */
+       for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
+               if (flash_devices[index].device_id == flash_id)
+                       return index;
+       }
+
+       /* Memory chip is not listed and not supported */
+       return -ENODEV;
+}
+
+/**
+ * spear_smi_write_enable - Enable the flash to do write operation
+ * @dev: structure of SMI device
+ * @bank: enable write for flash connected to this bank
+ *
+ * Set write enable latch with Write Enable command.
+ * Returns 0 on success.
+ */
+static int spear_smi_write_enable(struct spear_smi *dev, u32 bank)
+{
+       int ret;
+       u32 ctrlreg1;
+
+       mutex_lock(&dev->lock);
+       dev->status = 0; /* Will be set in interrupt handler */
+
+       ctrlreg1 = readl(dev->io_base + SMI_CR1);
+       /* program smi in h/w mode */
+       writel(ctrlreg1 & ~SW_MODE, dev->io_base + SMI_CR1);
+
+       /* give the flash, write enable command */
+       writel((bank << BANK_SHIFT) | WE | TFIE, dev->io_base + SMI_CR2);
+
+       ret = wait_event_interruptible_timeout(dev->cmd_complete,
+                       dev->status & TFF, SMI_CMD_TIMEOUT);
+
+       /* restore the ctrl regs state */
+       writel(ctrlreg1, dev->io_base + SMI_CR1);
+       writel(0, dev->io_base + SMI_CR2);
+
+       if (ret <= 0) {
+               ret = -EIO;
+               dev_err(&dev->pdev->dev,
+                       "smi controller failed on write enable\n");
+       } else {
+               /* check whether write mode status is set for required bank */
+               if (dev->status & (1 << (bank + WM_SHIFT)))
+                       ret = 0;
+               else {
+                       dev_err(&dev->pdev->dev, "couldn't enable write\n");
+                       ret = -EIO;
+               }
+       }
+
+       mutex_unlock(&dev->lock);
+       return ret;
+}
+
+static inline u32
+get_sector_erase_cmd(struct spear_snor_flash *flash, u32 offset)
+{
+       u32 cmd;
+       u8 *x = (u8 *)&cmd;
+
+       x[0] = flash->erase_cmd;
+       x[1] = offset >> 16;
+       x[2] = offset >> 8;
+       x[3] = offset;
+
+       return cmd;
+}
+
+/**
+ * spear_smi_erase_sector - erase one sector of flash
+ * @dev: structure of SMI information
+ * @command: erase command to be send
+ * @bank: bank to which this command needs to be send
+ * @bytes: size of command
+ *
+ * Erase one sector of flash memory at offset ``offset'' which is any
+ * address within the sector which should be erased.
+ * Returns 0 if successful, non-zero otherwise.
+ */
+static int spear_smi_erase_sector(struct spear_smi *dev,
+               u32 bank, u32 command, u32 bytes)
+{
+       u32 ctrlreg1 = 0;
+       int ret;
+
+       ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT);
+       if (ret)
+               return ret;
+
+       ret = spear_smi_write_enable(dev, bank);
+       if (ret)
+               return ret;
+
+       mutex_lock(&dev->lock);
+
+       ctrlreg1 = readl(dev->io_base + SMI_CR1);
+       writel((ctrlreg1 | SW_MODE) & ~WB_MODE, dev->io_base + SMI_CR1);
+
+       /* send command in sw mode */
+       writel(command, dev->io_base + SMI_TR);
+
+       writel((bank << BANK_SHIFT) | SEND | TFIE | (bytes << TX_LEN_SHIFT),
+                       dev->io_base + SMI_CR2);
+
+       ret = wait_event_interruptible_timeout(dev->cmd_complete,
+                       dev->status & TFF, SMI_CMD_TIMEOUT);
+
+       if (ret <= 0) {
+               ret = -EIO;
+               dev_err(&dev->pdev->dev, "sector erase failed\n");
+       } else
+               ret = 0; /* success */
+
+       /* restore ctrl regs */
+       writel(ctrlreg1, dev->io_base + SMI_CR1);
+       writel(0, dev->io_base + SMI_CR2);
+
+       mutex_unlock(&dev->lock);
+       return ret;
+}
+
+/**
+ * spear_mtd_erase - perform flash erase operation as requested by user
+ * @mtd: Provides the memory characteristics
+ * @e_info: Provides the erase information
+ *
+ * Erase an address range on the flash chip. The address range may extend
+ * one or more erase sectors. Return an error is there is a problem erasing.
+ */
+static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
+{
+       struct spear_snor_flash *flash = get_flash_data(mtd);
+       struct spear_smi *dev = mtd->priv;
+       u32 addr, command, bank;
+       int len, ret;
+
+       if (!flash || !dev)
+               return -ENODEV;
+
+       bank = flash->bank;
+       if (bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid Bank Num");
+               return -EINVAL;
+       }
+
+       addr = e_info->addr;
+       len = e_info->len;
+
+       mutex_lock(&flash->lock);
+
+       /* now erase sectors in loop */
+       while (len) {
+               command = get_sector_erase_cmd(flash, addr);
+               /* preparing the command for flash */
+               ret = spear_smi_erase_sector(dev, bank, command, 4);
+               if (ret) {
+                       e_info->state = MTD_ERASE_FAILED;
+                       mutex_unlock(&flash->lock);
+                       return ret;
+               }
+               addr += mtd->erasesize;
+               len -= mtd->erasesize;
+       }
+
+       mutex_unlock(&flash->lock);
+       e_info->state = MTD_ERASE_DONE;
+       mtd_erase_callback(e_info);
+
+       return 0;
+}
+
+/**
+ * spear_mtd_read - performs flash read operation as requested by the user
+ * @mtd: MTD information of the memory bank
+ * @from: Address from which to start read
+ * @len: Number of bytes to be read
+ * @retlen: Fills the Number of bytes actually read
+ * @buf: Fills this after reading
+ *
+ * Read an address range from the flash chip. The address range
+ * may be any size provided it is within the physical boundaries.
+ * Returns 0 on success, non zero otherwise
+ */
+static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u8 *buf)
+{
+       struct spear_snor_flash *flash = get_flash_data(mtd);
+       struct spear_smi *dev = mtd->priv;
+       void *src;
+       u32 ctrlreg1, val;
+       int ret;
+
+       if (!flash || !dev)
+               return -ENODEV;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid Bank Num");
+               return -EINVAL;
+       }
+
+       /* select address as per bank number */
+       src = flash->base_addr + from;
+
+       mutex_lock(&flash->lock);
+
+       /* wait till previous write/erase is done. */
+       ret = spear_smi_wait_till_ready(dev, flash->bank, SMI_MAX_TIME_OUT);
+       if (ret) {
+               mutex_unlock(&flash->lock);
+               return ret;
+       }
+
+       mutex_lock(&dev->lock);
+       /* put smi in hw mode not wbt mode */
+       ctrlreg1 = val = readl(dev->io_base + SMI_CR1);
+       val &= ~(SW_MODE | WB_MODE);
+       if (flash->fast_mode)
+               val |= FAST_MODE;
+
+       writel(val, dev->io_base + SMI_CR1);
+
+       memcpy_fromio(buf, (u8 *)src, len);
+
+       /* restore ctrl reg1 */
+       writel(ctrlreg1, dev->io_base + SMI_CR1);
+       mutex_unlock(&dev->lock);
+
+       *retlen = len;
+       mutex_unlock(&flash->lock);
+
+       return 0;
+}
+
+static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,
+               void *dest, const void *src, size_t len)
+{
+       int ret;
+       u32 ctrlreg1;
+
+       /* wait until finished previous write command. */
+       ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT);
+       if (ret)
+               return ret;
+
+       /* put smi in write enable */
+       ret = spear_smi_write_enable(dev, bank);
+       if (ret)
+               return ret;
+
+       /* put smi in hw, write burst mode */
+       mutex_lock(&dev->lock);
+
+       ctrlreg1 = readl(dev->io_base + SMI_CR1);
+       writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1);
+
+       memcpy_toio(dest, src, len);
+
+       writel(ctrlreg1, dev->io_base + SMI_CR1);
+
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+/**
+ * spear_mtd_write - performs write operation as requested by the user.
+ * @mtd: MTD information of the memory bank.
+ * @to:        Address to write.
+ * @len: Number of bytes to be written.
+ * @retlen: Number of bytes actually wrote.
+ * @buf: Buffer from which the data to be taken.
+ *
+ * Write an address range to the flash chip. Data must be written in
+ * flash_page_size chunks. The address range may be any size provided
+ * it is within the physical boundaries.
+ * Returns 0 on success, non zero otherwise
+ */
+static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
+               size_t *retlen, const u8 *buf)
+{
+       struct spear_snor_flash *flash = get_flash_data(mtd);
+       struct spear_smi *dev = mtd->priv;
+       void *dest;
+       u32 page_offset, page_size;
+       int ret;
+
+       if (!flash || !dev)
+               return -ENODEV;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid Bank Num");
+               return -EINVAL;
+       }
+
+       /* select address as per bank number */
+       dest = flash->base_addr + to;
+       mutex_lock(&flash->lock);
+
+       page_offset = (u32)to % flash->page_size;
+
+       /* do if all the bytes fit onto one page */
+       if (page_offset + len <= flash->page_size) {
+               ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, len);
+               if (!ret)
+                       *retlen += len;
+       } else {
+               u32 i;
+
+               /* the size of data remaining on the first page */
+               page_size = flash->page_size - page_offset;
+
+               ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf,
+                               page_size);
+               if (ret)
+                       goto err_write;
+               else
+                       *retlen += page_size;
+
+               /* write everything in pagesize chunks */
+               for (i = page_size; i < len; i += page_size) {
+                       page_size = len - i;
+                       if (page_size > flash->page_size)
+                               page_size = flash->page_size;
+
+                       ret = spear_smi_cpy_toio(dev, flash->bank, dest + i,
+                                       buf + i, page_size);
+                       if (ret)
+                               break;
+                       else
+                               *retlen += page_size;
+               }
+       }
+
+err_write:
+       mutex_unlock(&flash->lock);
+
+       return ret;
+}
+
+/**
+ * spear_smi_probe_flash - Detects the NOR Flash chip.
+ * @dev: structure of SMI information.
+ * @bank: bank on which flash must be probed
+ *
+ * This routine will check whether there exists a flash chip on a given memory
+ * bank ID.
+ * Return index of the probed flash in flash devices structure
+ */
+static int spear_smi_probe_flash(struct spear_smi *dev, u32 bank)
+{
+       int ret;
+       u32 val = 0;
+
+       ret = spear_smi_wait_till_ready(dev, bank, SMI_PROBE_TIMEOUT);
+       if (ret)
+               return ret;
+
+       mutex_lock(&dev->lock);
+
+       dev->status = 0; /* Will be set in interrupt handler */
+       /* put smi in sw mode */
+       val = readl(dev->io_base + SMI_CR1);
+       writel(val | SW_MODE, dev->io_base + SMI_CR1);
+
+       /* send readid command in sw mode */
+       writel(OPCODE_RDID, dev->io_base + SMI_TR);
+
+       val = (bank << BANK_SHIFT) | SEND | (1 << TX_LEN_SHIFT) |
+               (3 << RX_LEN_SHIFT) | TFIE;
+       writel(val, dev->io_base + SMI_CR2);
+
+       /* wait for TFF */
+       ret = wait_event_interruptible_timeout(dev->cmd_complete,
+                       dev->status & TFF, SMI_CMD_TIMEOUT);
+       if (ret <= 0) {
+               ret = -ENODEV;
+               goto err_probe;
+       }
+
+       /* get memory chip id */
+       val = readl(dev->io_base + SMI_RR);
+       val &= 0x00ffffff;
+       ret = get_flash_index(val);
+
+err_probe:
+       /* clear sw mode */
+       val = readl(dev->io_base + SMI_CR1);
+       writel(val & ~SW_MODE, dev->io_base + SMI_CR1);
+
+       mutex_unlock(&dev->lock);
+       return ret;
+}
+
+
+#ifdef CONFIG_OF
+static int __devinit spear_smi_probe_config_dt(struct platform_device *pdev,
+                                              struct device_node *np)
+{
+       struct spear_smi_plat_data *pdata = dev_get_platdata(&pdev->dev);
+       struct device_node *pp = NULL;
+       const __be32 *addr;
+       u32 val;
+       int len;
+       int i = 0;
+
+       if (!np)
+               return -ENODEV;
+
+       of_property_read_u32(np, "clock-rate", &val);
+       pdata->clk_rate = val;
+
+       pdata->board_flash_info = devm_kzalloc(&pdev->dev,
+                                              sizeof(*pdata->board_flash_info),
+                                              GFP_KERNEL);
+
+       /* Fill structs for each subnode (flash device) */
+       while ((pp = of_get_next_child(np, pp))) {
+               struct spear_smi_flash_info *flash_info;
+
+               flash_info = &pdata->board_flash_info[i];
+               pdata->np[i] = pp;
+
+               /* Read base-addr and size from DT */
+               addr = of_get_property(pp, "reg", &len);
+               pdata->board_flash_info->mem_base = be32_to_cpup(&addr[0]);
+               pdata->board_flash_info->size = be32_to_cpup(&addr[1]);
+
+               if (of_get_property(pp, "st,smi-fast-mode", NULL))
+                       pdata->board_flash_info->fast_mode = 1;
+
+               i++;
+       }
+
+       pdata->num_flashes = i;
+
+       return 0;
+}
+#else
+static int __devinit spear_smi_probe_config_dt(struct platform_device *pdev,
+                                              struct device_node *np)
+{
+       return -ENOSYS;
+}
+#endif
+
+static int spear_smi_setup_banks(struct platform_device *pdev,
+                                u32 bank, struct device_node *np)
+{
+       struct spear_smi *dev = platform_get_drvdata(pdev);
+       struct mtd_part_parser_data ppdata = {};
+       struct spear_smi_flash_info *flash_info;
+       struct spear_smi_plat_data *pdata;
+       struct spear_snor_flash *flash;
+       struct mtd_partition *parts = NULL;
+       int count = 0;
+       int flash_index;
+       int ret = 0;
+
+       pdata = dev_get_platdata(&pdev->dev);
+       if (bank > pdata->num_flashes - 1)
+               return -EINVAL;
+
+       flash_info = &pdata->board_flash_info[bank];
+       if (!flash_info)
+               return -ENODEV;
+
+       flash = kzalloc(sizeof(*flash), GFP_ATOMIC);
+       if (!flash)
+               return -ENOMEM;
+       flash->bank = bank;
+       flash->fast_mode = flash_info->fast_mode ? 1 : 0;
+       mutex_init(&flash->lock);
+
+       /* verify whether nor flash is really present on board */
+       flash_index = spear_smi_probe_flash(dev, bank);
+       if (flash_index < 0) {
+               dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank);
+               ret = flash_index;
+               goto err_probe;
+       }
+       /* map the memory for nor flash chip */
+       flash->base_addr = ioremap(flash_info->mem_base, flash_info->size);
+       if (!flash->base_addr) {
+               ret = -EIO;
+               goto err_probe;
+       }
+
+       dev->flash[bank] = flash;
+       flash->mtd.priv = dev;
+
+       if (flash_info->name)
+               flash->mtd.name = flash_info->name;
+       else
+               flash->mtd.name = flash_devices[flash_index].name;
+
+       flash->mtd.type = MTD_NORFLASH;
+       flash->mtd.writesize = 1;
+       flash->mtd.flags = MTD_CAP_NORFLASH;
+       flash->mtd.size = flash_info->size;
+       flash->mtd.erasesize = flash_devices[flash_index].sectorsize;
+       flash->page_size = flash_devices[flash_index].pagesize;
+       flash->mtd.writebufsize = flash->page_size;
+       flash->erase_cmd = flash_devices[flash_index].erase_cmd;
+       flash->mtd._erase = spear_mtd_erase;
+       flash->mtd._read = spear_mtd_read;
+       flash->mtd._write = spear_mtd_write;
+       flash->dev_id = flash_devices[flash_index].device_id;
+
+       dev_info(&dev->pdev->dev, "mtd .name=%s .size=%llx(%lluM)\n",
+                       flash->mtd.name, flash->mtd.size,
+                       flash->mtd.size / (1024 * 1024));
+
+       dev_info(&dev->pdev->dev, ".erasesize = 0x%x(%uK)\n",
+                       flash->mtd.erasesize, flash->mtd.erasesize / 1024);
+
+#ifndef CONFIG_OF
+       if (flash_info->partitions) {
+               parts = flash_info->partitions;
+               count = flash_info->nr_partitions;
+       }
+#endif
+       ppdata.of_node = np;
+
+       ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
+                                       count);
+       if (ret) {
+               dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret);
+               goto err_map;
+       }
+
+       return 0;
+
+err_map:
+       iounmap(flash->base_addr);
+
+err_probe:
+       kfree(flash);
+       return ret;
+}
+
+/**
+ * spear_smi_probe - Entry routine
+ * @pdev: platform device structure
+ *
+ * This is the first routine which gets invoked during booting and does all
+ * initialization/allocation work. The routine looks for available memory banks,
+ * and do proper init for any found one.
+ * Returns 0 on success, non zero otherwise
+ */
+static int __devinit spear_smi_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct spear_smi_plat_data *pdata = NULL;
+       struct spear_smi *dev;
+       struct resource *smi_base;
+       int irq, ret = 0;
+       int i;
+
+       if (np) {
+               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+               if (!pdata) {
+                       pr_err("%s: ERROR: no memory", __func__);
+                       ret = -ENOMEM;
+                       goto err;
+               }
+               pdev->dev.platform_data = pdata;
+               ret = spear_smi_probe_config_dt(pdev, np);
+               if (ret) {
+                       ret = -ENODEV;
+                       dev_err(&pdev->dev, "no platform data\n");
+                       goto err;
+               }
+       } else {
+               pdata = dev_get_platdata(&pdev->dev);
+               if (pdata < 0) {
+                       ret = -ENODEV;
+                       dev_err(&pdev->dev, "no platform data\n");
+                       goto err;
+               }
+       }
+
+       smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!smi_base) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "invalid smi base address\n");
+               goto err;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "invalid smi irq\n");
+               goto err;
+       }
+
+       dev = kzalloc(sizeof(*dev), GFP_ATOMIC);
+       if (!dev) {
+               ret = -ENOMEM;
+               dev_err(&pdev->dev, "mem alloc fail\n");
+               goto err;
+       }
+
+       smi_base = request_mem_region(smi_base->start, resource_size(smi_base),
+                       pdev->name);
+       if (!smi_base) {
+               ret = -EBUSY;
+               dev_err(&pdev->dev, "request mem region fail\n");
+               goto err_mem;
+       }
+
+       dev->io_base = ioremap(smi_base->start, resource_size(smi_base));
+       if (!dev->io_base) {
+               ret = -EIO;
+               dev_err(&pdev->dev, "ioremap fail\n");
+               goto err_ioremap;
+       }
+
+       dev->pdev = pdev;
+       dev->clk_rate = pdata->clk_rate;
+
+       if (dev->clk_rate < 0 || dev->clk_rate > SMI_MAX_CLOCK_FREQ)
+               dev->clk_rate = SMI_MAX_CLOCK_FREQ;
+
+       dev->num_flashes = pdata->num_flashes;
+
+       if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
+               dev_err(&pdev->dev, "exceeding max number of flashes\n");
+               dev->num_flashes = MAX_NUM_FLASH_CHIP;
+       }
+
+       dev->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dev->clk)) {
+               ret = PTR_ERR(dev->clk);
+               goto err_clk;
+       }
+
+       ret = clk_enable(dev->clk);
+       if (ret)
+               goto err_clk_enable;
+
+       ret = request_irq(irq, spear_smi_int_handler, 0, pdev->name, dev);
+       if (ret) {
+               dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n");
+               goto err_irq;
+       }
+
+       mutex_init(&dev->lock);
+       init_waitqueue_head(&dev->cmd_complete);
+       spear_smi_hw_init(dev);
+       platform_set_drvdata(pdev, dev);
+
+       /* loop for each serial nor-flash which is connected to smi */
+       for (i = 0; i < dev->num_flashes; i++) {
+               ret = spear_smi_setup_banks(pdev, i, pdata->np[i]);
+               if (ret) {
+                       dev_err(&dev->pdev->dev, "bank setup failed\n");
+                       goto err_bank_setup;
+               }
+       }
+
+       return 0;
+
+err_bank_setup:
+       free_irq(irq, dev);
+       platform_set_drvdata(pdev, NULL);
+err_irq:
+       clk_disable(dev->clk);
+err_clk_enable:
+       clk_put(dev->clk);
+err_clk:
+       iounmap(dev->io_base);
+err_ioremap:
+       release_mem_region(smi_base->start, resource_size(smi_base));
+err_mem:
+       kfree(dev);
+err:
+       return ret;
+}
+
+/**
+ * spear_smi_remove - Exit routine
+ * @pdev: platform device structure
+ *
+ * free all allocations and delete the partitions.
+ */
+static int __devexit spear_smi_remove(struct platform_device *pdev)
+{
+       struct spear_smi *dev;
+       struct spear_smi_plat_data *pdata;
+       struct spear_snor_flash *flash;
+       struct resource *smi_base;
+       int ret;
+       int i, irq;
+
+       dev = platform_get_drvdata(pdev);
+       if (!dev) {
+               dev_err(&pdev->dev, "dev is null\n");
+               return -ENODEV;
+       }
+
+       pdata = dev_get_platdata(&pdev->dev);
+
+       /* clean up for all nor flash */
+       for (i = 0; i < dev->num_flashes; i++) {
+               flash = dev->flash[i];
+               if (!flash)
+                       continue;
+
+               /* clean up mtd stuff */
+               ret = mtd_device_unregister(&flash->mtd);
+               if (ret)
+                       dev_err(&pdev->dev, "error removing mtd\n");
+
+               iounmap(flash->base_addr);
+               kfree(flash);
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       free_irq(irq, dev);
+
+       clk_disable(dev->clk);
+       clk_put(dev->clk);
+       iounmap(dev->io_base);
+       kfree(dev);
+
+       smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(smi_base->start, resource_size(smi_base));
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+int spear_smi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct spear_smi *dev = platform_get_drvdata(pdev);
+
+       if (dev && dev->clk)
+               clk_disable(dev->clk);
+
+       return 0;
+}
+
+int spear_smi_resume(struct platform_device *pdev)
+{
+       struct spear_smi *dev = platform_get_drvdata(pdev);
+       int ret = -EPERM;
+
+       if (dev && dev->clk)
+               ret = clk_enable(dev->clk);
+
+       if (!ret)
+               spear_smi_hw_init(dev);
+       return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spear_smi_id_table[] = {
+       { .compatible = "st,spear600-smi" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, spear_smi_id_table);
+#endif
+
+static struct platform_driver spear_smi_driver = {
+       .driver = {
+               .name = "smi",
+               .bus = &platform_bus_type,
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(spear_smi_id_table),
+       },
+       .probe = spear_smi_probe,
+       .remove = __devexit_p(spear_smi_remove),
+       .suspend = spear_smi_suspend,
+       .resume = spear_smi_resume,
+};
+
+static int spear_smi_init(void)
+{
+       return platform_driver_register(&spear_smi_driver);
+}
+module_init(spear_smi_init);
+
+static void spear_smi_exit(void)
+{
+       platform_driver_unregister(&spear_smi_driver);
+}
+module_exit(spear_smi_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.hashim@st.com>");
+MODULE_DESCRIPTION("MTD SMI driver for serial nor flash chips");
index 5fc198350b94b66ea27b5e7113f4f192c61c9dfd..ab8a2f4c8d60cfac01c332e9e4aa9e488d6f7f11 100644 (file)
@@ -175,9 +175,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
        int err;
 
        /* Sanity checks */
-       if (instr->addr + instr->len > flash->mtd.size)
-               return -EINVAL;
-
        if ((uint32_t)instr->len % mtd->erasesize)
                return -EINVAL;
 
@@ -223,16 +220,6 @@ static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len,
        unsigned char command[4];
        int ret;
 
-       /* Sanity checking */
-       if (len == 0)
-               return 0;
-
-       if (from + len > flash->mtd.size)
-               return -EINVAL;
-
-       if (retlen)
-               *retlen = 0;
-
        spi_message_init(&message);
        memset(&transfer, 0, sizeof(transfer));
 
@@ -274,13 +261,6 @@ static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len,
        int i, j, ret, bytes, copied = 0;
        unsigned char command[5];
 
-       /* Sanity checks */
-       if (!len)
-               return 0;
-
-       if (to + len > flash->mtd.size)
-               return -EINVAL;
-
        if ((uint32_t)to % mtd->writesize)
                return -EINVAL;
 
@@ -402,10 +382,11 @@ static int __devinit sst25l_probe(struct spi_device *spi)
        flash->mtd.flags        = MTD_CAP_NORFLASH;
        flash->mtd.erasesize    = flash_info->erase_size;
        flash->mtd.writesize    = flash_info->page_size;
+       flash->mtd.writebufsize = flash_info->page_size;
        flash->mtd.size         = flash_info->page_size * flash_info->nr_pages;
-       flash->mtd.erase        = sst25l_erase;
-       flash->mtd.read         = sst25l_read;
-       flash->mtd.write        = sst25l_write;
+       flash->mtd._erase       = sst25l_erase;
+       flash->mtd._read                = sst25l_read;
+       flash->mtd._write       = sst25l_write;
 
        dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name,
                 (long long)flash->mtd.size >> 10);
@@ -418,9 +399,9 @@ static int __devinit sst25l_probe(struct spi_device *spi)
              flash->mtd.numeraseregions);
 
 
-       ret = mtd_device_parse_register(&flash->mtd, NULL, 0,
-                       data ? data->parts : NULL,
-                       data ? data->nr_parts : 0);
+       ret = mtd_device_parse_register(&flash->mtd, NULL, NULL,
+                                       data ? data->parts : NULL,
+                                       data ? data->nr_parts : 0);
        if (ret) {
                kfree(flash);
                dev_set_drvdata(&spi->dev, NULL);
@@ -450,18 +431,7 @@ static struct spi_driver sst25l_driver = {
        .remove         = __devexit_p(sst25l_remove),
 };
 
-static int __init sst25l_init(void)
-{
-       return spi_register_driver(&sst25l_driver);
-}
-
-static void __exit sst25l_exit(void)
-{
-       spi_unregister_driver(&sst25l_driver);
-}
-
-module_init(sst25l_init);
-module_exit(sst25l_exit);
+module_spi_driver(sst25l_driver);
 
 MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips");
 MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, "
index 28646c95cfb845803fd983c6a83b489bf4130c41..3af351484098b6b47c5b0973dda092abdc1bfe0d 100644 (file)
@@ -56,7 +56,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        if (memcmp(mtd->name, "DiskOnChip", 10))
                return;
 
-       if (!mtd->block_isbad) {
+       if (!mtd->_block_isbad) {
                printk(KERN_ERR
 "INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
 "Please use the new diskonchip driver under the NAND subsystem.\n");
index 536bbceaeaad559868acf633a6cd994412af3c4a..d3cfe26beeaa6bcc771a085f1e3f41d641af4c19 100644 (file)
@@ -40,7 +40,7 @@ static int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 static int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
                        size_t *retlen, void **mtdbuf, resource_size_t *phys);
-static void lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len);
+static int lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len);
 static int get_chip(struct map_info *map, struct flchip *chip, int mode);
 static int chip_ready(struct map_info *map, struct flchip *chip, int mode);
 static void put_chip(struct map_info *map, struct flchip *chip);
@@ -63,18 +63,18 @@ struct mtd_info *lpddr_cmdset(struct map_info *map)
        mtd->type = MTD_NORFLASH;
 
        /* Fill in the default mtd operations */
-       mtd->read = lpddr_read;
+       mtd->_read = lpddr_read;
        mtd->type = MTD_NORFLASH;
        mtd->flags = MTD_CAP_NORFLASH;
        mtd->flags &= ~MTD_BIT_WRITEABLE;
-       mtd->erase = lpddr_erase;
-       mtd->write = lpddr_write_buffers;
-       mtd->writev = lpddr_writev;
-       mtd->lock = lpddr_lock;
-       mtd->unlock = lpddr_unlock;
+       mtd->_erase = lpddr_erase;
+       mtd->_write = lpddr_write_buffers;
+       mtd->_writev = lpddr_writev;
+       mtd->_lock = lpddr_lock;
+       mtd->_unlock = lpddr_unlock;
        if (map_is_linear(map)) {
-               mtd->point = lpddr_point;
-               mtd->unpoint = lpddr_unpoint;
+               mtd->_point = lpddr_point;
+               mtd->_unpoint = lpddr_unpoint;
        }
        mtd->size = 1 << lpddr->qinfo->DevSizeShift;
        mtd->erasesize = 1 << lpddr->qinfo->UniformBlockSizeShift;
@@ -530,14 +530,12 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
        struct flchip *chip = &lpddr->chips[chipnum];
        int ret = 0;
 
-       if (!map->virt || (adr + len > mtd->size))
+       if (!map->virt)
                return -EINVAL;
 
        /* ofs: offset within the first chip that the first read should start */
        ofs = adr - (chipnum << lpddr->chipshift);
-
        *mtdbuf = (void *)map->virt + chip->start + ofs;
-       *retlen = 0;
 
        while (len) {
                unsigned long thislen;
@@ -575,11 +573,11 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
        return 0;
 }
 
-static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
+static int lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
 {
        struct map_info *map = mtd->priv;
        struct lpddr_private *lpddr = map->fldrv_priv;
-       int chipnum = adr >> lpddr->chipshift;
+       int chipnum = adr >> lpddr->chipshift, err = 0;
        unsigned long ofs;
 
        /* ofs: offset within the first chip that the first read should start */
@@ -603,9 +601,11 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
                        chip->ref_point_counter--;
                        if (chip->ref_point_counter == 0)
                                chip->state = FL_READY;
-               } else
+               } else {
                        printk(KERN_WARNING "%s: Warning: unpoint called on non"
                                        "pointed region\n", map->name);
+                       err = -EINVAL;
+               }
 
                put_chip(map, chip);
                mutex_unlock(&chip->mutex);
@@ -614,6 +614,8 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
                ofs = 0;
                chipnum++;
        }
+
+       return err;
 }
 
 static int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
@@ -637,13 +639,11 @@ static int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs,
        int chipnum;
        unsigned long ofs, vec_seek, i;
        int wbufsize = 1 << lpddr->qinfo->BufSizeShift;
-
        size_t len = 0;
 
        for (i = 0; i < count; i++)
                len += vecs[i].iov_len;
 
-       *retlen = 0;
        if (!len)
                return 0;
 
@@ -688,9 +688,6 @@ static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr)
        ofs = instr->addr;
        len = instr->len;
 
-       if (ofs > mtd->size || (len + ofs) > mtd->size)
-               return -EINVAL;
-
        while (len > 0) {
                ret = do_erase_oneblock(mtd, ofs);
                if (ret)
index 650126c361f18865d9e29b346fdeb8650208f9b6..ef5cde84a8b3bb962f1d7291e00c761ebdee729c 100644 (file)
@@ -164,8 +164,8 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
-       mtd_device_parse_register(state->mtd, part_probe_types, 0,
-                       pdata->parts, pdata->nr_parts);
+       mtd_device_parse_register(state->mtd, part_probe_types, NULL,
+                                 pdata->parts, pdata->nr_parts);
 
        platform_set_drvdata(pdev, state);
 
index f43b365b848c41ebf2e1a8614762de835b84bbbe..080f06053bd449d51467f3ab294acfe05289ed34 100644 (file)
@@ -196,7 +196,7 @@ static int __init init_dc21285(void)
 
        dc21285_mtd->owner = THIS_MODULE;
 
-       mtd_device_parse_register(dc21285_mtd, probes, 0, NULL, 0);
+       mtd_device_parse_register(dc21285_mtd, probes, NULL, NULL, 0);
 
        if(machine_is_ebsa285()) {
                /*
index 33cce895859f0f8959cea03f06f28d24d9567b26..e4de96ba52b3529117418e842d48bdee064de94c 100644 (file)
@@ -252,8 +252,8 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
        }
 
 
-       mtd_device_parse_register(state->mtd, part_probe_types, 0,
-                       pdata->parts, pdata->nr_parts);
+       mtd_device_parse_register(state->mtd, part_probe_types, NULL,
+                                 pdata->parts, pdata->nr_parts);
 
        return 0;
 }
index 49c14187fc66dc0e469f071db2971d2c0ca4be10..8ed6cb4529d82996b4981d9cff366b55673cb88b 100644 (file)
@@ -85,8 +85,8 @@ static int __init h720x_mtd_init(void)
        if (mymtd) {
                mymtd->owner = THIS_MODULE;
 
-               mtd_device_parse_register(mymtd, NULL, 0,
-                               h720x_partitions, NUM_PARTITIONS);
+               mtd_device_parse_register(mymtd, NULL, NULL,
+                                         h720x_partitions, NUM_PARTITIONS);
                return 0;
        }
 
index f47aedb24366bb0f1073a1d6c5803a103f029709..834a06c56f565ca466276474aebb2aea360fb97b 100644 (file)
@@ -91,7 +91,7 @@ static int __init init_impa7(void)
                if (impa7_mtd[i]) {
                        impa7_mtd[i]->owner = THIS_MODULE;
                        devicesfound++;
-                       mtd_device_parse_register(impa7_mtd[i], NULL, 0,
+                       mtd_device_parse_register(impa7_mtd[i], NULL, NULL,
                                                  partitions,
                                                  ARRAY_SIZE(partitions));
                }
index 08c239604ee44e1b71438feda4d30f065b0663f6..92e1f41634c7135ffc1b39d50c0323f0de33c603 100644 (file)
@@ -72,7 +72,7 @@ static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p)
 {
        /* register the flash bank */
        /* partition the flash bank */
-       return mtd_device_parse_register(p->info, NULL, 0, NULL, 0);
+       return mtd_device_parse_register(p->info, NULL, NULL, NULL, 0);
 }
 
 static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
index fc7d4d0d9a4e4c31532577705f65209ffc1dbc7b..4a41ced0f71099045fcbd40f3db6aa9bd10ca401 100644 (file)
@@ -226,7 +226,7 @@ static int ixp2000_flash_probe(struct platform_device *dev)
        }
        info->mtd->owner = THIS_MODULE;
 
-       err = mtd_device_parse_register(info->mtd, probes, 0, NULL, 0);
+       err = mtd_device_parse_register(info->mtd, probes, NULL, NULL, 0);
        if (err)
                goto Error;
 
index 8b5410162d70edecfa94277f361c9e5bf28c782a..e864fc6c58f9ccd900aad7284570ef70ab919220 100644 (file)
@@ -182,6 +182,9 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
 {
        struct flash_platform_data *plat = dev->dev.platform_data;
        struct ixp4xx_flash_info *info;
+       struct mtd_part_parser_data ppdata = {
+               .origin = dev->resource->start,
+       };
        int err = -1;
 
        if (!plat)
@@ -247,7 +250,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
        /* Use the fast version */
        info->map.write = ixp4xx_write16;
 
-       err = mtd_device_parse_register(info->mtd, probes, dev->resource->start,
+       err = mtd_device_parse_register(info->mtd, probes, &ppdata,
                        plat->parts, plat->nr_parts);
        if (err) {
                printk(KERN_ERR "Could not parse partitions\n");
index dd0360ba2412b646e27dd05fd11fbe29c37e211a..74bd98ee635fa96e60973490f1f54650f54cfca3 100644 (file)
@@ -27,17 +27,21 @@ static struct mtd_info *mymtd;
 
 
 /* Is this really the vpp port? */
+static DEFINE_SPINLOCK(l440gx_vpp_lock);
+static int l440gx_vpp_refcnt;
 static void l440gx_set_vpp(struct map_info *map, int vpp)
 {
-       unsigned long l;
+       unsigned long flags;
 
-       l = inl(VPP_PORT);
+       spin_lock_irqsave(&l440gx_vpp_lock, flags);
        if (vpp) {
-               l |= 1;
+               if (++l440gx_vpp_refcnt == 1)   /* first nested 'on' */
+                       outl(inl(VPP_PORT) | 1, VPP_PORT);
        } else {
-               l &= ~1;
+               if (--l440gx_vpp_refcnt == 0)   /* last nested 'off' */
+                       outl(inl(VPP_PORT) & ~1, VPP_PORT);
        }
-       outl(l, VPP_PORT);
+       spin_unlock_irqrestore(&l440gx_vpp_lock, flags);
 }
 
 static struct map_info l440gx_map = {
index 7b889de9477bcf4b0bf6c9936c38e6246e039a3a..b5401e355745bfd4ce1e92732151e5fd34e0e9e0 100644 (file)
@@ -45,6 +45,7 @@ struct ltq_mtd {
 };
 
 static char ltq_map_name[] = "ltq_nor";
+static const char *ltq_probe_types[] __devinitconst = { "cmdlinepart", NULL };
 
 static map_word
 ltq_read16(struct map_info *map, unsigned long adr)
@@ -168,8 +169,9 @@ ltq_mtd_probe(struct platform_device *pdev)
        cfi->addr_unlock1 ^= 1;
        cfi->addr_unlock2 ^= 1;
 
-       err = mtd_device_parse_register(ltq_mtd->mtd, NULL, 0,
-                       ltq_mtd_data->parts, ltq_mtd_data->nr_parts);
+       err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types, NULL,
+                                       ltq_mtd_data->parts,
+                                       ltq_mtd_data->nr_parts);
        if (err) {
                dev_err(&pdev->dev, "failed to add partitions\n");
                goto err_destroy;
index 8fed58e3a4a808e9423f3e36b02eeb7e5b5276e2..3c7ad17fca78e06a593f350e3463d113b17dfa76 100644 (file)
@@ -199,8 +199,9 @@ static int __devinit latch_addr_flash_probe(struct platform_device *dev)
        }
        info->mtd->owner = THIS_MODULE;
 
-       mtd_device_parse_register(info->mtd, NULL, 0,
-                       latch_addr_data->parts, latch_addr_data->nr_parts);
+       mtd_device_parse_register(info->mtd, NULL, NULL,
+                                 latch_addr_data->parts,
+                                 latch_addr_data->nr_parts);
        return 0;
 
 iounmap:
index 0259cf5830222e4c2da75f921be90c8517bd8c15..a3cfad392ed653c1543a5318ddfc40f007b72233 100644 (file)
@@ -294,13 +294,24 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f
 }
 
 
+static DEFINE_SPINLOCK(pcmcia_vpp_lock);
+static int pcmcia_vpp_refcnt;
 static void pcmciamtd_set_vpp(struct map_info *map, int on)
 {
        struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
        struct pcmcia_device *link = dev->p_dev;
+       unsigned long flags;
 
        pr_debug("dev = %p on = %d vpp = %d\n\n", dev, on, dev->vpp);
-       pcmcia_fixup_vpp(link, on ? dev->vpp : 0);
+       spin_lock_irqsave(&pcmcia_vpp_lock, flags);
+       if (on) {
+               if (++pcmcia_vpp_refcnt == 1)   /* first nested 'on' */
+                       pcmcia_fixup_vpp(link, dev->vpp);
+       } else {
+               if (--pcmcia_vpp_refcnt == 0)   /* last nested 'off' */
+                       pcmcia_fixup_vpp(link, 0);
+       }
+       spin_unlock_irqrestore(&pcmcia_vpp_lock, flags);
 }
 
 
index abc562653b31777d4ea6d4217335d14b64ec8b15..21b0b713cacb8fd8cb1870b6341ac69c08354d19 100644 (file)
@@ -27,6 +27,8 @@ struct physmap_flash_info {
        struct mtd_info         *mtd[MAX_RESOURCES];
        struct mtd_info         *cmtd;
        struct map_info         map[MAX_RESOURCES];
+       spinlock_t              vpp_lock;
+       int                     vpp_refcnt;
 };
 
 static int physmap_flash_remove(struct platform_device *dev)
@@ -63,12 +65,26 @@ static void physmap_set_vpp(struct map_info *map, int state)
 {
        struct platform_device *pdev;
        struct physmap_flash_data *physmap_data;
+       struct physmap_flash_info *info;
+       unsigned long flags;
 
        pdev = (struct platform_device *)map->map_priv_1;
        physmap_data = pdev->dev.platform_data;
 
-       if (physmap_data->set_vpp)
-               physmap_data->set_vpp(pdev, state);
+       if (!physmap_data->set_vpp)
+               return;
+
+       info = platform_get_drvdata(pdev);
+
+       spin_lock_irqsave(&info->vpp_lock, flags);
+       if (state) {
+               if (++info->vpp_refcnt == 1)    /* first nested 'on' */
+                       physmap_data->set_vpp(pdev, 1);
+       } else {
+               if (--info->vpp_refcnt == 0)    /* last nested 'off' */
+                       physmap_data->set_vpp(pdev, 0);
+       }
+       spin_unlock_irqrestore(&info->vpp_lock, flags);
 }
 
 static const char *rom_probe_types[] = {
@@ -172,9 +188,11 @@ static int physmap_flash_probe(struct platform_device *dev)
        if (err)
                goto err_out;
 
+       spin_lock_init(&info->vpp_lock);
+
        part_types = physmap_data->part_probe_types ? : part_probe_types;
 
-       mtd_device_parse_register(info->cmtd, part_types, 0,
+       mtd_device_parse_register(info->cmtd, part_types, NULL,
                                  physmap_data->parts, physmap_data->nr_parts);
        return 0;
 
index 45876d0e5b8e500b321466fad5789d7a9bfbe182..891558de3ec19d1ef67ca69751be8f06e21e480f 100644 (file)
@@ -222,8 +222,9 @@ static int platram_probe(struct platform_device *pdev)
        /* check to see if there are any available partitions, or wether
         * to add this device whole */
 
-       err = mtd_device_parse_register(info->mtd, pdata->probes, 0,
-                       pdata->partitions, pdata->nr_partitions);
+       err = mtd_device_parse_register(info->mtd, pdata->probes, NULL,
+                                       pdata->partitions,
+                                       pdata->nr_partitions);
        if (!err)
                dev_info(&pdev->dev, "registered mtd device\n");
 
index 436d121185b15a230878f6091ab5041270fe6d92..81884c277405e9d35d114e75c2e5f5d90e714652 100644 (file)
@@ -98,7 +98,8 @@ static int __devinit pxa2xx_flash_probe(struct platform_device *pdev)
        }
        info->mtd->owner = THIS_MODULE;
 
-       mtd_device_parse_register(info->mtd, probes, 0, flash->parts, flash->nr_parts);
+       mtd_device_parse_register(info->mtd, probes, NULL, flash->parts,
+                                 flash->nr_parts);
 
        platform_set_drvdata(pdev, info);
        return 0;
index 3da63fc6f16eb4faf522fdaa5b230b18c70c7354..6f52e1f288b674074f3045667c35678a09cb6f98 100644 (file)
@@ -102,8 +102,8 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
        info->mtd->owner = THIS_MODULE;
        if (err)
                goto err_out;
-       err = mtd_device_parse_register(info->mtd, NULL, 0,
-                       pdata->parts, pdata->nr_parts);
+       err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts,
+                                       pdata->nr_parts);
 
        if (err)
                goto err_out;
index cbc3b7867910ac421488c9531fbd3799875ea41b..a675bdbcb0fe882790c47a9929c55b4b382e7577 100644 (file)
@@ -36,10 +36,22 @@ struct sa_info {
        struct sa_subdev_info   subdev[0];
 };
 
+static DEFINE_SPINLOCK(sa1100_vpp_lock);
+static int sa1100_vpp_refcnt;
 static void sa1100_set_vpp(struct map_info *map, int on)
 {
        struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
-       subdev->plat->set_vpp(on);
+       unsigned long flags;
+
+       spin_lock_irqsave(&sa1100_vpp_lock, flags);
+       if (on) {
+               if (++sa1100_vpp_refcnt == 1)   /* first nested 'on' */
+                       subdev->plat->set_vpp(1);
+       } else {
+               if (--sa1100_vpp_refcnt == 0)   /* last nested 'off' */
+                       subdev->plat->set_vpp(0);
+       }
+       spin_unlock_irqrestore(&sa1100_vpp_lock, flags);
 }
 
 static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
@@ -252,8 +264,8 @@ static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
        /*
         * Partition selection stuff.
         */
-       mtd_device_parse_register(info->mtd, part_probes, 0,
-                       plat->parts, plat->nr_parts);
+       mtd_device_parse_register(info->mtd, part_probes, NULL, plat->parts,
+                                 plat->nr_parts);
 
        platform_set_drvdata(pdev, info);
        err = 0;
index 496c40704aff6567dcd7546b0cb9c87742222263..9d900ada67084535daa04aa5dd1effb7d9053857 100644 (file)
@@ -92,8 +92,8 @@ static int __init init_soleng_maps(void)
                mtd_device_register(eprom_mtd, NULL, 0);
        }
 
-       mtd_device_parse_register(flash_mtd, probes, 0,
-                       superh_se_partitions, NUM_PARTITIONS);
+       mtd_device_parse_register(flash_mtd, probes, NULL,
+                                 superh_se_partitions, NUM_PARTITIONS);
 
        return 0;
 }
index 6793074f3f40aff0eacb4d4e94eb6c8e960e51e7..cfff454f628ba73498f0876355e9c1266d67c7fa 100644 (file)
@@ -85,7 +85,7 @@ static int __init uclinux_mtd_init(void)
        }
 
        mtd->owner = THIS_MODULE;
-       mtd->point = uclinux_point;
+       mtd->_point = uclinux_point;
        mtd->priv = mapp;
 
        uclinux_ram_mtdinfo = mtd;
index 3a04b078576a0aa04ee07afa4fec48c56fe3f43b..2e2b0945edc7622159a3a2f8a1391d75c574eec8 100644 (file)
@@ -360,9 +360,6 @@ static int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
        int index = 0, retval, partition, leftover, numblocks;
        unsigned char cx;
 
-       if (len < 1)
-               return -EIO;
-
        mpart = mtd->priv;
        mdev = mpart->mdev;
        partition = mpart->partition;
@@ -434,11 +431,6 @@ static int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
        partition = mpart->partition;
        card = maple_get_drvdata(mdev);
 
-       /* simple sanity checks */
-       if (len < 1) {
-               error = -EIO;
-               goto failed;
-       }
        numblocks = card->parts[partition].numblocks;
        if (to + len > numblocks * card->blocklen)
                len = numblocks * card->blocklen - to;
@@ -544,9 +536,9 @@ static void vmu_queryblocks(struct mapleq *mq)
        mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE;
        mtd_cur->size = part_cur->numblocks * card->blocklen;
        mtd_cur->erasesize = card->blocklen;
-       mtd_cur->write = vmu_flash_write;
-       mtd_cur->read = vmu_flash_read;
-       mtd_cur->sync = vmu_flash_sync;
+       mtd_cur->_write = vmu_flash_write;
+       mtd_cur->_read = vmu_flash_read;
+       mtd_cur->_sync = vmu_flash_sync;
        mtd_cur->writesize = card->blocklen;
 
        mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL);
index aa7e0cb2893c791311e71356ec79d074745129a6..71b0ba7979121f8cde4480751f547d42d8fa858b 100644 (file)
@@ -142,7 +142,7 @@ static int __init init_sbc82xx_flash(void)
                        nr_parts = ARRAY_SIZE(smallflash_parts);
                }
 
-               mtd_device_parse_register(sbcmtd[i], part_probes, 0,
+               mtd_device_parse_register(sbcmtd[i], part_probes, NULL,
                                          defparts, nr_parts);
        }
        return 0;
index 424ca5f93c6c37f74304f04a2e393523ae453f7a..f1f06715d4e0b8e3f800e456ef83aa3d27fee3ce 100644 (file)
@@ -233,6 +233,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
        ret = __get_mtd_device(dev->mtd);
        if (ret)
                goto error_release;
+       dev->file_mode = mode;
 
 unlock:
        dev->open++;
index af6591237b9b51f22022e61a653fa7a5d8e29845..6c6d80736fadfb4013871b31795e17354a4d671e 100644 (file)
@@ -321,8 +321,12 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
        mutex_unlock(&mtdblk->cache_mutex);
 
        if (!--mtdblk->count) {
-               /* It was the last usage. Free the cache */
-               mtd_sync(mbd->mtd);
+               /*
+                * It was the last usage. Free the cache, but only sync if
+                * opened for writing.
+                */
+               if (mbd->file_mode & FMODE_WRITE)
+                       mtd_sync(mbd->mtd);
                vfree(mtdblk->cache_data);
        }
 
index 28c585ffc8afa1208835c5a512e04bc0f1d34334..94eb05b1afdfd2bcab367a6e6fa6dbab0667dca9 100644 (file)
@@ -412,7 +412,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
        if (length > 4096)
                return -EINVAL;
 
-       if (!mtd->write_oob)
+       if (!mtd->_write_oob)
                ret = -EOPNOTSUPP;
        else
                ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT;
@@ -583,7 +583,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
                        !access_ok(VERIFY_READ, req.usr_data, req.len) ||
                        !access_ok(VERIFY_READ, req.usr_oob, req.ooblen))
                return -EFAULT;
-       if (!mtd->write_oob)
+       if (!mtd->_write_oob)
                return -EOPNOTSUPP;
 
        ops.mode = req.mode;
index 1ed5103b219ba3d51919b3ae285eb64f335e1af9..b9000563b9f4311d63ffdfe66ba5166eb158b482 100644 (file)
@@ -72,8 +72,6 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
        int ret = 0, err;
        int i;
 
-       *retlen = 0;
-
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
                size_t size, retsize;
@@ -126,11 +124,6 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
        int err = -EINVAL;
        int i;
 
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-
-       *retlen = 0;
-
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
                size_t size, retsize;
@@ -145,11 +138,7 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
                else
                        size = len;
 
-               if (!(subdev->flags & MTD_WRITEABLE))
-                       err = -EROFS;
-               else
-                       err = mtd_write(subdev, to, size, &retsize, buf);
-
+               err = mtd_write(subdev, to, size, &retsize, buf);
                if (err)
                        break;
 
@@ -176,19 +165,10 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
        int i;
        int err = -EINVAL;
 
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-
-       *retlen = 0;
-
        /* Calculate total length of data */
        for (i = 0; i < count; i++)
                total_len += vecs[i].iov_len;
 
-       /* Do not allow write past end of device */
-       if ((to + total_len) > mtd->size)
-               return -EINVAL;
-
        /* Check alignment */
        if (mtd->writesize > 1) {
                uint64_t __to = to;
@@ -224,12 +204,8 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
                old_iov_len = vecs_copy[entry_high].iov_len;
                vecs_copy[entry_high].iov_len = size;
 
-               if (!(subdev->flags & MTD_WRITEABLE))
-                       err = -EROFS;
-               else
-                       err = mtd_writev(subdev, &vecs_copy[entry_low],
-                                        entry_high - entry_low + 1, to,
-                                        &retsize);
+               err = mtd_writev(subdev, &vecs_copy[entry_low],
+                                entry_high - entry_low + 1, to, &retsize);
 
                vecs_copy[entry_high].iov_len = old_iov_len - size;
                vecs_copy[entry_high].iov_base += size;
@@ -403,15 +379,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
        uint64_t length, offset = 0;
        struct erase_info *erase;
 
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-
-       if (instr->addr > concat->mtd.size)
-               return -EINVAL;
-
-       if (instr->len + instr->addr > concat->mtd.size)
-               return -EINVAL;
-
        /*
         * Check for proper erase block alignment of the to-be-erased area.
         * It is easier to do this based on the super device's erase
@@ -459,8 +426,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
                        return -EINVAL;
        }
 
-       instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
-
        /* make a local copy of instr to avoid modifying the caller's struct */
        erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
 
@@ -499,10 +464,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
                else
                        erase->len = length;
 
-               if (!(subdev->flags & MTD_WRITEABLE)) {
-                       err = -EROFS;
-                       break;
-               }
                length -= erase->len;
                if ((err = concat_dev_erase(subdev, erase))) {
                        /* sanity check: should never happen since
@@ -538,9 +499,6 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        struct mtd_concat *concat = CONCAT(mtd);
        int i, err = -EINVAL;
 
-       if ((len + ofs) > mtd->size)
-               return -EINVAL;
-
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
                uint64_t size;
@@ -575,9 +533,6 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        struct mtd_concat *concat = CONCAT(mtd);
        int i, err = 0;
 
-       if ((len + ofs) > mtd->size)
-               return -EINVAL;
-
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
                uint64_t size;
@@ -650,9 +605,6 @@ static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
        if (!mtd_can_have_bb(concat->subdev[0]))
                return res;
 
-       if (ofs > mtd->size)
-               return -EINVAL;
-
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
 
@@ -673,12 +625,6 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
        struct mtd_concat *concat = CONCAT(mtd);
        int i, err = -EINVAL;
 
-       if (!mtd_can_have_bb(concat->subdev[0]))
-               return 0;
-
-       if (ofs > mtd->size)
-               return -EINVAL;
-
        for (i = 0; i < concat->num_subdev; i++) {
                struct mtd_info *subdev = concat->subdev[i];
 
@@ -716,10 +662,6 @@ static unsigned long concat_get_unmapped_area(struct mtd_info *mtd,
                        continue;
                }
 
-               /* we've found the subdev over which the mapping will reside */
-               if (offset + len > subdev->size)
-                       return (unsigned long) -EINVAL;
-
                return mtd_get_unmapped_area(subdev, len, offset, flags);
        }
 
@@ -777,16 +719,16 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],     /* subdevices to c
        concat->mtd.subpage_sft = subdev[0]->subpage_sft;
        concat->mtd.oobsize = subdev[0]->oobsize;
        concat->mtd.oobavail = subdev[0]->oobavail;
-       if (subdev[0]->writev)
-               concat->mtd.writev = concat_writev;
-       if (subdev[0]->read_oob)
-               concat->mtd.read_oob = concat_read_oob;
-       if (subdev[0]->write_oob)
-               concat->mtd.write_oob = concat_write_oob;
-       if (subdev[0]->block_isbad)
-               concat->mtd.block_isbad = concat_block_isbad;
-       if (subdev[0]->block_markbad)
-               concat->mtd.block_markbad = concat_block_markbad;
+       if (subdev[0]->_writev)
+               concat->mtd._writev = concat_writev;
+       if (subdev[0]->_read_oob)
+               concat->mtd._read_oob = concat_read_oob;
+       if (subdev[0]->_write_oob)
+               concat->mtd._write_oob = concat_write_oob;
+       if (subdev[0]->_block_isbad)
+               concat->mtd._block_isbad = concat_block_isbad;
+       if (subdev[0]->_block_markbad)
+               concat->mtd._block_markbad = concat_block_markbad;
 
        concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
 
@@ -833,8 +775,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
                if (concat->mtd.writesize   !=  subdev[i]->writesize ||
                    concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
                    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
-                   !concat->mtd.read_oob  != !subdev[i]->read_oob ||
-                   !concat->mtd.write_oob != !subdev[i]->write_oob) {
+                   !concat->mtd._read_oob  != !subdev[i]->_read_oob ||
+                   !concat->mtd._write_oob != !subdev[i]->_write_oob) {
                        kfree(concat);
                        printk("Incompatible OOB or ECC data on \"%s\"\n",
                               subdev[i]->name);
@@ -849,15 +791,15 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],     /* subdevices to c
        concat->num_subdev = num_devs;
        concat->mtd.name = name;
 
-       concat->mtd.erase = concat_erase;
-       concat->mtd.read = concat_read;
-       concat->mtd.write = concat_write;
-       concat->mtd.sync = concat_sync;
-       concat->mtd.lock = concat_lock;
-       concat->mtd.unlock = concat_unlock;
-       concat->mtd.suspend = concat_suspend;
-       concat->mtd.resume = concat_resume;
-       concat->mtd.get_unmapped_area = concat_get_unmapped_area;
+       concat->mtd._erase = concat_erase;
+       concat->mtd._read = concat_read;
+       concat->mtd._write = concat_write;
+       concat->mtd._sync = concat_sync;
+       concat->mtd._lock = concat_lock;
+       concat->mtd._unlock = concat_unlock;
+       concat->mtd._suspend = concat_suspend;
+       concat->mtd._resume = concat_resume;
+       concat->mtd._get_unmapped_area = concat_get_unmapped_area;
 
        /*
         * Combine the erase block size info of the subdevices:
index 9a9ce71a71fcbb2e004c0c0eb2f9a680202ee30d..c837507dfb1c73021da2a47eebd975bd40573ef9 100644 (file)
@@ -107,7 +107,7 @@ static LIST_HEAD(mtd_notifiers);
  */
 static void mtd_release(struct device *dev)
 {
-       struct mtd_info *mtd = dev_get_drvdata(dev);
+       struct mtd_info __maybe_unused *mtd = dev_get_drvdata(dev);
        dev_t index = MTD_DEVT(mtd->index);
 
        /* remove /dev/mtdXro node if needed */
@@ -126,7 +126,7 @@ static int mtd_cls_resume(struct device *dev)
 {
        struct mtd_info *mtd = dev_get_drvdata(dev);
 
-       if (mtd && mtd->resume)
+       if (mtd)
                mtd_resume(mtd);
        return 0;
 }
@@ -610,8 +610,8 @@ int __get_mtd_device(struct mtd_info *mtd)
        if (!try_module_get(mtd->owner))
                return -ENODEV;
 
-       if (mtd->get_device) {
-               err = mtd->get_device(mtd);
+       if (mtd->_get_device) {
+               err = mtd->_get_device(mtd);
 
                if (err) {
                        module_put(mtd->owner);
@@ -675,13 +675,266 @@ void __put_mtd_device(struct mtd_info *mtd)
        --mtd->usecount;
        BUG_ON(mtd->usecount < 0);
 
-       if (mtd->put_device)
-               mtd->put_device(mtd);
+       if (mtd->_put_device)
+               mtd->_put_device(mtd);
 
        module_put(mtd->owner);
 }
 EXPORT_SYMBOL_GPL(__put_mtd_device);
 
+/*
+ * Erase is an asynchronous operation.  Device drivers are supposed
+ * to call instr->callback() whenever the operation completes, even
+ * if it completes with a failure.
+ * Callers are supposed to pass a callback function and wait for it
+ * to be called before writing to the block.
+ */
+int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+       if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr)
+               return -EINVAL;
+       if (!(mtd->flags & MTD_WRITEABLE))
+               return -EROFS;
+       instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+       if (!instr->len) {
+               instr->state = MTD_ERASE_DONE;
+               mtd_erase_callback(instr);
+               return 0;
+       }
+       return mtd->_erase(mtd, instr);
+}
+EXPORT_SYMBOL_GPL(mtd_erase);
+
+/*
+ * This stuff for eXecute-In-Place. phys is optional and may be set to NULL.
+ */
+int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+             void **virt, resource_size_t *phys)
+{
+       *retlen = 0;
+       *virt = NULL;
+       if (phys)
+               *phys = 0;
+       if (!mtd->_point)
+               return -EOPNOTSUPP;
+       if (from < 0 || from > mtd->size || len > mtd->size - from)
+               return -EINVAL;
+       if (!len)
+               return 0;
+       return mtd->_point(mtd, from, len, retlen, virt, phys);
+}
+EXPORT_SYMBOL_GPL(mtd_point);
+
+/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
+int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+{
+       if (!mtd->_point)
+               return -EOPNOTSUPP;
+       if (from < 0 || from > mtd->size || len > mtd->size - from)
+               return -EINVAL;
+       if (!len)
+               return 0;
+       return mtd->_unpoint(mtd, from, len);
+}
+EXPORT_SYMBOL_GPL(mtd_unpoint);
+
+/*
+ * Allow NOMMU mmap() to directly map the device (if not NULL)
+ * - return the address to which the offset maps
+ * - return -ENOSYS to indicate refusal to do the mapping
+ */
+unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len,
+                                   unsigned long offset, unsigned long flags)
+{
+       if (!mtd->_get_unmapped_area)
+               return -EOPNOTSUPP;
+       if (offset > mtd->size || len > mtd->size - offset)
+               return -EINVAL;
+       return mtd->_get_unmapped_area(mtd, len, offset, flags);
+}
+EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
+
+int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+            u_char *buf)
+{
+       *retlen = 0;
+       if (from < 0 || from > mtd->size || len > mtd->size - from)
+               return -EINVAL;
+       if (!len)
+               return 0;
+       return mtd->_read(mtd, from, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_read);
+
+int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+             const u_char *buf)
+{
+       *retlen = 0;
+       if (to < 0 || to > mtd->size || len > mtd->size - to)
+               return -EINVAL;
+       if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE))
+               return -EROFS;
+       if (!len)
+               return 0;
+       return mtd->_write(mtd, to, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_write);
+
+/*
+ * In blackbox flight recorder like scenarios we want to make successful writes
+ * in interrupt context. panic_write() is only intended to be called when its
+ * known the kernel is about to panic and we need the write to succeed. Since
+ * the kernel is not going to be running for much longer, this function can
+ * break locks and delay to ensure the write succeeds (but not sleep).
+ */
+int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+                   const u_char *buf)
+{
+       *retlen = 0;
+       if (!mtd->_panic_write)
+               return -EOPNOTSUPP;
+       if (to < 0 || to > mtd->size || len > mtd->size - to)
+               return -EINVAL;
+       if (!(mtd->flags & MTD_WRITEABLE))
+               return -EROFS;
+       if (!len)
+               return 0;
+       return mtd->_panic_write(mtd, to, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_panic_write);
+
+/*
+ * Method to access the protection register area, present in some flash
+ * devices. The user data is one time programmable but the factory data is read
+ * only.
+ */
+int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+                          size_t len)
+{
+       if (!mtd->_get_fact_prot_info)
+               return -EOPNOTSUPP;
+       if (!len)
+               return 0;
+       return mtd->_get_fact_prot_info(mtd, buf, len);
+}
+EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
+
+int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+                          size_t *retlen, u_char *buf)
+{
+       *retlen = 0;
+       if (!mtd->_read_fact_prot_reg)
+               return -EOPNOTSUPP;
+       if (!len)
+               return 0;
+       return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
+
+int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+                          size_t len)
+{
+       if (!mtd->_get_user_prot_info)
+               return -EOPNOTSUPP;
+       if (!len)
+               return 0;
+       return mtd->_get_user_prot_info(mtd, buf, len);
+}
+EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
+
+int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+                          size_t *retlen, u_char *buf)
+{
+       *retlen = 0;
+       if (!mtd->_read_user_prot_reg)
+               return -EOPNOTSUPP;
+       if (!len)
+               return 0;
+       return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
+
+int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
+                           size_t *retlen, u_char *buf)
+{
+       *retlen = 0;
+       if (!mtd->_write_user_prot_reg)
+               return -EOPNOTSUPP;
+       if (!len)
+               return 0;
+       return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
+
+int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
+{
+       if (!mtd->_lock_user_prot_reg)
+               return -EOPNOTSUPP;
+       if (!len)
+               return 0;
+       return mtd->_lock_user_prot_reg(mtd, from, len);
+}
+EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg);
+
+/* Chip-supported device locking */
+int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+       if (!mtd->_lock)
+               return -EOPNOTSUPP;
+       if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
+               return -EINVAL;
+       if (!len)
+               return 0;
+       return mtd->_lock(mtd, ofs, len);
+}
+EXPORT_SYMBOL_GPL(mtd_lock);
+
+int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+       if (!mtd->_unlock)
+               return -EOPNOTSUPP;
+       if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
+               return -EINVAL;
+       if (!len)
+               return 0;
+       return mtd->_unlock(mtd, ofs, len);
+}
+EXPORT_SYMBOL_GPL(mtd_unlock);
+
+int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+       if (!mtd->_is_locked)
+               return -EOPNOTSUPP;
+       if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
+               return -EINVAL;
+       if (!len)
+               return 0;
+       return mtd->_is_locked(mtd, ofs, len);
+}
+EXPORT_SYMBOL_GPL(mtd_is_locked);
+
+int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+       if (!mtd->_block_isbad)
+               return 0;
+       if (ofs < 0 || ofs > mtd->size)
+               return -EINVAL;
+       return mtd->_block_isbad(mtd, ofs);
+}
+EXPORT_SYMBOL_GPL(mtd_block_isbad);
+
+int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+       if (!mtd->_block_markbad)
+               return -EOPNOTSUPP;
+       if (ofs < 0 || ofs > mtd->size)
+               return -EINVAL;
+       if (!(mtd->flags & MTD_WRITEABLE))
+               return -EROFS;
+       return mtd->_block_markbad(mtd, ofs);
+}
+EXPORT_SYMBOL_GPL(mtd_block_markbad);
+
 /*
  * default_mtd_writev - the default writev method
  * @mtd: mtd device description object pointer
@@ -729,9 +982,11 @@ int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
               unsigned long count, loff_t to, size_t *retlen)
 {
        *retlen = 0;
-       if (!mtd->writev)
+       if (!(mtd->flags & MTD_WRITEABLE))
+               return -EROFS;
+       if (!mtd->_writev)
                return default_mtd_writev(mtd, vecs, count, to, retlen);
-       return mtd->writev(mtd, vecs, count, to, retlen);
+       return mtd->_writev(mtd, vecs, count, to, retlen);
 }
 EXPORT_SYMBOL_GPL(mtd_writev);
 
index 3ce99e00a49e89f558796c5a426934bb97e91a84..ae36d7e1e91368dd36239a57f73ef4ccda637f56 100644 (file)
@@ -169,7 +169,7 @@ static void mtdoops_workfunc_erase(struct work_struct *work)
                        cxt->nextpage = 0;
        }
 
-       while (mtd_can_have_bb(mtd)) {
+       while (1) {
                ret = mtd_block_isbad(mtd, cxt->nextpage * record_size);
                if (!ret)
                        break;
@@ -199,9 +199,9 @@ badblock:
                return;
        }
 
-       if (mtd_can_have_bb(mtd) && ret == -EIO) {
+       if (ret == -EIO) {
                ret = mtd_block_markbad(mtd, cxt->nextpage * record_size);
-               if (ret < 0) {
+               if (ret < 0 && ret != -EOPNOTSUPP) {
                        printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n");
                        return;
                }
@@ -257,8 +257,7 @@ static void find_next_position(struct mtdoops_context *cxt)
        size_t retlen;
 
        for (page = 0; page < cxt->oops_pages; page++) {
-               if (mtd_can_have_bb(mtd) &&
-                   mtd_block_isbad(mtd, page * record_size))
+               if (mtd_block_isbad(mtd, page * record_size))
                        continue;
                /* Assume the page is used */
                mark_page_used(cxt, page);
index a3d44c3416b4f2bef8a1932976b2825a1e230609..9651c06de0a9298f4db58265524ecaebe2c2b9b4 100644 (file)
@@ -65,12 +65,8 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
        int res;
 
        stats = part->master->ecc_stats;
-
-       if (from >= mtd->size)
-               len = 0;
-       else if (from + len > mtd->size)
-               len = mtd->size - from;
-       res = mtd_read(part->master, from + part->offset, len, retlen, buf);
+       res = part->master->_read(part->master, from + part->offset, len,
+                                 retlen, buf);
        if (unlikely(res)) {
                if (mtd_is_bitflip(res))
                        mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
@@ -84,19 +80,16 @@ static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, void **virt, resource_size_t *phys)
 {
        struct mtd_part *part = PART(mtd);
-       if (from >= mtd->size)
-               len = 0;
-       else if (from + len > mtd->size)
-               len = mtd->size - from;
-       return mtd_point(part->master, from + part->offset, len, retlen,
-                        virt, phys);
+
+       return part->master->_point(part->master, from + part->offset, len,
+                                   retlen, virt, phys);
 }
 
-static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 {
        struct mtd_part *part = PART(mtd);
 
-       mtd_unpoint(part->master, from + part->offset, len);
+       return part->master->_unpoint(part->master, from + part->offset, len);
 }
 
 static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
@@ -107,7 +100,8 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
        struct mtd_part *part = PART(mtd);
 
        offset += part->offset;
-       return mtd_get_unmapped_area(part->master, len, offset, flags);
+       return part->master->_get_unmapped_area(part->master, len, offset,
+                                               flags);
 }
 
 static int part_read_oob(struct mtd_info *mtd, loff_t from,
@@ -138,7 +132,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
                        return -EINVAL;
        }
 
-       res = mtd_read_oob(part->master, from + part->offset, ops);
+       res = part->master->_read_oob(part->master, from + part->offset, ops);
        if (unlikely(res)) {
                if (mtd_is_bitflip(res))
                        mtd->ecc_stats.corrected++;
@@ -152,55 +146,46 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
                size_t len, size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return mtd_read_user_prot_reg(part->master, from, len, retlen, buf);
+       return part->master->_read_user_prot_reg(part->master, from, len,
+                                                retlen, buf);
 }
 
 static int part_get_user_prot_info(struct mtd_info *mtd,
                struct otp_info *buf, size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       return mtd_get_user_prot_info(part->master, buf, len);
+       return part->master->_get_user_prot_info(part->master, buf, len);
 }
 
 static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
                size_t len, size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return mtd_read_fact_prot_reg(part->master, from, len, retlen, buf);
+       return part->master->_read_fact_prot_reg(part->master, from, len,
+                                                retlen, buf);
 }
 
 static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
                size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       return mtd_get_fact_prot_info(part->master, buf, len);
+       return part->master->_get_fact_prot_info(part->master, buf, len);
 }
 
 static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-       if (to >= mtd->size)
-               len = 0;
-       else if (to + len > mtd->size)
-               len = mtd->size - to;
-       return mtd_write(part->master, to + part->offset, len, retlen, buf);
+       return part->master->_write(part->master, to + part->offset, len,
+                                   retlen, buf);
 }
 
 static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-       if (to >= mtd->size)
-               len = 0;
-       else if (to + len > mtd->size)
-               len = mtd->size - to;
-       return mtd_panic_write(part->master, to + part->offset, len, retlen,
-                              buf);
+       return part->master->_panic_write(part->master, to + part->offset, len,
+                                         retlen, buf);
 }
 
 static int part_write_oob(struct mtd_info *mtd, loff_t to,
@@ -208,50 +193,43 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
 {
        struct mtd_part *part = PART(mtd);
 
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-
        if (to >= mtd->size)
                return -EINVAL;
        if (ops->datbuf && to + ops->len > mtd->size)
                return -EINVAL;
-       return mtd_write_oob(part->master, to + part->offset, ops);
+       return part->master->_write_oob(part->master, to + part->offset, ops);
 }
 
 static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
                size_t len, size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return mtd_write_user_prot_reg(part->master, from, len, retlen, buf);
+       return part->master->_write_user_prot_reg(part->master, from, len,
+                                                 retlen, buf);
 }
 
 static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
                size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       return mtd_lock_user_prot_reg(part->master, from, len);
+       return part->master->_lock_user_prot_reg(part->master, from, len);
 }
 
 static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
                unsigned long count, loff_t to, size_t *retlen)
 {
        struct mtd_part *part = PART(mtd);
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-       return mtd_writev(part->master, vecs, count, to + part->offset,
-                         retlen);
+       return part->master->_writev(part->master, vecs, count,
+                                    to + part->offset, retlen);
 }
 
 static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct mtd_part *part = PART(mtd);
        int ret;
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-       if (instr->addr >= mtd->size)
-               return -EINVAL;
+
        instr->addr += part->offset;
-       ret = mtd_erase(part->master, instr);
+       ret = part->master->_erase(part->master, instr);
        if (ret) {
                if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
                        instr->fail_addr -= part->offset;
@@ -262,7 +240,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
 
 void mtd_erase_callback(struct erase_info *instr)
 {
-       if (instr->mtd->erase == part_erase) {
+       if (instr->mtd->_erase == part_erase) {
                struct mtd_part *part = PART(instr->mtd);
 
                if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
@@ -277,52 +255,44 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback);
 static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_part *part = PART(mtd);
-       if ((len + ofs) > mtd->size)
-               return -EINVAL;
-       return mtd_lock(part->master, ofs + part->offset, len);
+       return part->master->_lock(part->master, ofs + part->offset, len);
 }
 
 static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_part *part = PART(mtd);
-       if ((len + ofs) > mtd->size)
-               return -EINVAL;
-       return mtd_unlock(part->master, ofs + part->offset, len);
+       return part->master->_unlock(part->master, ofs + part->offset, len);
 }
 
 static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_part *part = PART(mtd);
-       if ((len + ofs) > mtd->size)
-               return -EINVAL;
-       return mtd_is_locked(part->master, ofs + part->offset, len);
+       return part->master->_is_locked(part->master, ofs + part->offset, len);
 }
 
 static void part_sync(struct mtd_info *mtd)
 {
        struct mtd_part *part = PART(mtd);
-       mtd_sync(part->master);
+       part->master->_sync(part->master);
 }
 
 static int part_suspend(struct mtd_info *mtd)
 {
        struct mtd_part *part = PART(mtd);
-       return mtd_suspend(part->master);
+       return part->master->_suspend(part->master);
 }
 
 static void part_resume(struct mtd_info *mtd)
 {
        struct mtd_part *part = PART(mtd);
-       mtd_resume(part->master);
+       part->master->_resume(part->master);
 }
 
 static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
 {
        struct mtd_part *part = PART(mtd);
-       if (ofs >= mtd->size)
-               return -EINVAL;
        ofs += part->offset;
-       return mtd_block_isbad(part->master, ofs);
+       return part->master->_block_isbad(part->master, ofs);
 }
 
 static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
@@ -330,12 +300,8 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
        struct mtd_part *part = PART(mtd);
        int res;
 
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-       if (ofs >= mtd->size)
-               return -EINVAL;
        ofs += part->offset;
-       res = mtd_block_markbad(part->master, ofs);
+       res = part->master->_block_markbad(part->master, ofs);
        if (!res)
                mtd->ecc_stats.badblocks++;
        return res;
@@ -410,54 +376,55 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
         */
        slave->mtd.dev.parent = master->dev.parent;
 
-       slave->mtd.read = part_read;
-       slave->mtd.write = part_write;
+       slave->mtd._read = part_read;
+       slave->mtd._write = part_write;
 
-       if (master->panic_write)
-               slave->mtd.panic_write = part_panic_write;
+       if (master->_panic_write)
+               slave->mtd._panic_write = part_panic_write;
 
-       if (master->point && master->unpoint) {
-               slave->mtd.point = part_point;
-               slave->mtd.unpoint = part_unpoint;
+       if (master->_point && master->_unpoint) {
+               slave->mtd._point = part_point;
+               slave->mtd._unpoint = part_unpoint;
        }
 
-       if (master->get_unmapped_area)
-               slave->mtd.get_unmapped_area = part_get_unmapped_area;
-       if (master->read_oob)
-               slave->mtd.read_oob = part_read_oob;
-       if (master->write_oob)
-               slave->mtd.write_oob = part_write_oob;
-       if (master->read_user_prot_reg)
-               slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
-       if (master->read_fact_prot_reg)
-               slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
-       if (master->write_user_prot_reg)
-               slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
-       if (master->lock_user_prot_reg)
-               slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
-       if (master->get_user_prot_info)
-               slave->mtd.get_user_prot_info = part_get_user_prot_info;
-       if (master->get_fact_prot_info)
-               slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
-       if (master->sync)
-               slave->mtd.sync = part_sync;
-       if (!partno && !master->dev.class && master->suspend && master->resume) {
-                       slave->mtd.suspend = part_suspend;
-                       slave->mtd.resume = part_resume;
+       if (master->_get_unmapped_area)
+               slave->mtd._get_unmapped_area = part_get_unmapped_area;
+       if (master->_read_oob)
+               slave->mtd._read_oob = part_read_oob;
+       if (master->_write_oob)
+               slave->mtd._write_oob = part_write_oob;
+       if (master->_read_user_prot_reg)
+               slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
+       if (master->_read_fact_prot_reg)
+               slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
+       if (master->_write_user_prot_reg)
+               slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
+       if (master->_lock_user_prot_reg)
+               slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
+       if (master->_get_user_prot_info)
+               slave->mtd._get_user_prot_info = part_get_user_prot_info;
+       if (master->_get_fact_prot_info)
+               slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
+       if (master->_sync)
+               slave->mtd._sync = part_sync;
+       if (!partno && !master->dev.class && master->_suspend &&
+           master->_resume) {
+                       slave->mtd._suspend = part_suspend;
+                       slave->mtd._resume = part_resume;
        }
-       if (master->writev)
-               slave->mtd.writev = part_writev;
-       if (master->lock)
-               slave->mtd.lock = part_lock;
-       if (master->unlock)
-               slave->mtd.unlock = part_unlock;
-       if (master->is_locked)
-               slave->mtd.is_locked = part_is_locked;
-       if (master->block_isbad)
-               slave->mtd.block_isbad = part_block_isbad;
-       if (master->block_markbad)
-               slave->mtd.block_markbad = part_block_markbad;
-       slave->mtd.erase = part_erase;
+       if (master->_writev)
+               slave->mtd._writev = part_writev;
+       if (master->_lock)
+               slave->mtd._lock = part_lock;
+       if (master->_unlock)
+               slave->mtd._unlock = part_unlock;
+       if (master->_is_locked)
+               slave->mtd._is_locked = part_is_locked;
+       if (master->_block_isbad)
+               slave->mtd._block_isbad = part_block_isbad;
+       if (master->_block_markbad)
+               slave->mtd._block_markbad = part_block_markbad;
+       slave->mtd._erase = part_erase;
        slave->master = master;
        slave->offset = part->offset;
 
@@ -549,7 +516,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
        }
 
        slave->mtd.ecclayout = master->ecclayout;
-       if (master->block_isbad) {
+       slave->mtd.ecc_strength = master->ecc_strength;
+       if (master->_block_isbad) {
                uint64_t offs = 0;
 
                while (offs < slave->mtd.size) {
@@ -761,7 +729,7 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
        for ( ; ret <= 0 && *types; types++) {
                parser = get_partition_parser(*types);
                if (!parser && !request_module("%s", *types))
-                               parser = get_partition_parser(*types);
+                       parser = get_partition_parser(*types);
                if (!parser)
                        continue;
                ret = (*parser->parse_fn)(master, pparts, data);
index a3c4de551ebee12239c07738f9d9896a3b26060f..7d17cecad69d8fccc1467eaa210c6fe2915197dd 100644 (file)
@@ -314,6 +314,26 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
          load time (assuming you build diskonchip as a module) with the module
          parameter "inftl_bbt_write=1".
 
+config MTD_NAND_DOCG4
+       tristate "Support for DiskOnChip G4 (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       select BCH
+       select BITREVERSE
+       help
+         Support for diskonchip G4 nand flash, found in various smartphones and
+         PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba
+         Portege G900, Asus P526, and O2 XDA Zinc.
+
+         With this driver you will be able to use UBI and create a ubifs on the
+         device, so you may wish to consider enabling UBI and UBIFS as well.
+
+         These devices ship with the Mys/Sandisk SAFTL formatting, for which
+         there is currently no mtd parser, so you may want to use command line
+         partitioning to segregate write-protected blocks. On the Treo680, the
+         first five erase blocks (256KiB each) are write-protected, followed
+         by the block containing the saftl partition table.  This is probably
+         typical.
+
 config MTD_NAND_SHARPSL
        tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
        depends on ARCH_PXA
@@ -421,7 +441,6 @@ config MTD_NAND_NANDSIM
 config MTD_NAND_GPMI_NAND
         bool "GPMI NAND Flash Controller driver"
         depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28)
-       select MTD_CMDLINE_PARTS
         help
         Enables NAND Flash support for IMX23 or IMX28.
         The GPMI controller is very powerful, with the help of BCH
index 19bc8cb1d1874bf3b64fbd6234258d5142171554..d4b4d8739bd8e88584e4b8f425d7cc9234b3e304 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
 obj-$(CONFIG_MTD_NAND_S3C2410)         += s3c2410.o
 obj-$(CONFIG_MTD_NAND_DAVINCI)         += davinci_nand.o
 obj-$(CONFIG_MTD_NAND_DISKONCHIP)      += diskonchip.o
+obj-$(CONFIG_MTD_NAND_DOCG4)           += docg4.o
 obj-$(CONFIG_MTD_NAND_FSMC)            += fsmc_nand.o
 obj-$(CONFIG_MTD_NAND_H1900)           += h1910.o
 obj-$(CONFIG_MTD_NAND_RTC_FROM4)       += rtc_from4.o
index 6a5ff64a139e6281e5b92a80f5c39066e81563ac..4f20e1d8bef10b43546bd64d51933c0ed010093d 100644 (file)
@@ -585,12 +585,13 @@ static int alauda_init_media(struct alauda *al)
        mtd->writesize = 1<<card->pageshift;
        mtd->type = MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->read = alauda_read;
-       mtd->write = alauda_write;
-       mtd->erase = alauda_erase;
-       mtd->block_isbad = alauda_isbad;
+       mtd->_read = alauda_read;
+       mtd->_write = alauda_write;
+       mtd->_erase = alauda_erase;
+       mtd->_block_isbad = alauda_isbad;
        mtd->priv = al;
        mtd->owner = THIS_MODULE;
+       mtd->ecc_strength = 1;
 
        err = mtd_device_register(mtd, NULL, 0);
        if (err) {
index ae7e37d9ac172fa411d99aacbb4c3b196cdc5991..2165576a1c67df0e623752970fdfdd141f3ea931 100644 (file)
@@ -603,6 +603,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
                nand_chip->ecc.hwctl = atmel_nand_hwctl;
                nand_chip->ecc.read_page = atmel_nand_read_page;
                nand_chip->ecc.bytes = 4;
+               nand_chip->ecc.strength = 1;
        }
 
        nand_chip->chip_delay = 20;             /* 20us command delay time */
index 64c9cbaf86a1905abdb8b8663740af687b883db3..6908cdde3065e73b24509e8c9d32865f5befb1d9 100644 (file)
@@ -475,6 +475,14 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
                        largepage_bbt.options = NAND_BBT_SCAN2NDPAGE;
                this->badblock_pattern = &largepage_bbt;
        }
+
+       /*
+        * FIXME: ecc strength value of 6 bits per 512 bytes of data is a
+        * conservative guess, given 13 ecc bytes and using bch alg.
+        * (Assume Galois field order m=15 to allow a margin of error.)
+        */
+       this->ecc.strength = 6;
+
 #endif
 
        /* Now finish off the scan, now that ecc.layout has been initialized. */
@@ -487,7 +495,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
 
        /* Register the partitions */
        board_mtd->name = "bcm_umi-nand";
-       mtd_device_parse_register(board_mtd, NULL, 0, NULL, 0);
+       mtd_device_parse_register(board_mtd, NULL, NULL, NULL, 0);
 
        /* Return happy */
        return 0;
index dd899cb5d366e4de044ec16129000bb99c6fcc58..d7b86b925de5ead4b4c0653bf13080bea55a4156 100644 (file)
@@ -702,9 +702,11 @@ static int bf5xx_nand_scan(struct mtd_info *mtd)
                if (likely(mtd->writesize >= 512)) {
                        chip->ecc.size = 512;
                        chip->ecc.bytes = 6;
+                       chip->ecc.strength = 2;
                } else {
                        chip->ecc.size = 256;
                        chip->ecc.bytes = 3;
+                       chip->ecc.strength = 1;
                        bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET));
                        SSYNC();
                }
index 72d3f23490c58a548c962b1771c44f3781560da9..2a96e1a12062314234f2cd74b86163029abc3e3d 100644 (file)
@@ -783,6 +783,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
        cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
        cafe->nand.ecc.size = mtd->writesize;
        cafe->nand.ecc.bytes = 14;
+       cafe->nand.ecc.strength = 4;
        cafe->nand.ecc.hwctl  = (void *)cafe_nand_bug;
        cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
        cafe->nand.ecc.correct  = (void *)cafe_nand_bug;
@@ -799,7 +800,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
        pci_set_drvdata(pdev, mtd);
 
        mtd->name = "cafe_nand";
-       mtd_device_parse_register(mtd, part_probes, 0, NULL, 0);
+       mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
 
        goto out;
 
index 737ef9a04fdbf35dc03253e49d26281ade9fff95..1024bfc05c8696485861e35b630522c44810d674 100644 (file)
@@ -219,7 +219,7 @@ static int __init cmx270_init(void)
        }
 
        /* Register the partitions */
-       ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, 0,
+       ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL,
                                        partition_info, NUM_PARTITIONS);
        if (ret)
                goto err_scan;
index 414afa7935637522709f6273f2df9930a1170a6a..821c34c6250021246dfdb7ef97a44e497a05726c 100644 (file)
@@ -248,6 +248,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
                goto out_ior;
        }
 
+       this->ecc.strength = 1;
+
        new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
 
        cs553x_mtd[cs] = new_mtd;
@@ -313,7 +315,7 @@ static int __init cs553x_init(void)
        for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
                if (cs553x_mtd[i]) {
                        /* If any devices registered, return success. Else the last error. */
-                       mtd_device_parse_register(cs553x_mtd[i], NULL, 0,
+                       mtd_device_parse_register(cs553x_mtd[i], NULL, NULL,
                                                  NULL, 0);
                        err = 0;
                }
index 6e566156956f2465e32b122d7728d9890ed98f08..d94b03c207af904f05b966783725d194b4d8b412 100644 (file)
@@ -641,6 +641,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
                        info->chip.ecc.bytes = 3;
                }
                info->chip.ecc.size = 512;
+               info->chip.ecc.strength = pdata->ecc_bits;
                break;
        default:
                ret = -EINVAL;
@@ -752,8 +753,8 @@ syndrome_done:
        if (ret < 0)
                goto err_scan;
 
-       ret = mtd_device_parse_register(&info->mtd, NULL, 0,
-                       pdata->parts, pdata->nr_parts);
+       ret = mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts,
+                                       pdata->nr_parts);
 
        if (ret < 0)
                goto err_scan;
index 3984d488f9abbf5a3d6ed77fd49e2c4b7dc79381..a9e57d686297096e0700935a90d6a5257aa789ff 100644 (file)
@@ -1590,6 +1590,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
                        ECC_15BITS * (denali->mtd.writesize /
                        ECC_SECTOR_SIZE)))) {
                /* if MLC OOB size is large enough, use 15bit ECC*/
+               denali->nand.ecc.strength = 15;
                denali->nand.ecc.layout = &nand_15bit_oob;
                denali->nand.ecc.bytes = ECC_15BITS;
                iowrite32(15, denali->flash_reg + ECC_CORRECTION);
@@ -1600,12 +1601,14 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
                                " contain 8bit ECC correction codes");
                goto failed_req_irq;
        } else {
+               denali->nand.ecc.strength = 8;
                denali->nand.ecc.layout = &nand_8bit_oob;
                denali->nand.ecc.bytes = ECC_8BITS;
                iowrite32(8, denali->flash_reg + ECC_CORRECTION);
        }
 
        denali->nand.ecc.bytes *= denali->devnum;
+       denali->nand.ecc.strength *= denali->devnum;
        denali->nand.ecc.layout->eccbytes *=
                denali->mtd.writesize / ECC_SECTOR_SIZE;
        denali->nand.ecc.layout->oobfree[0].offset =
index df921e7a496cb57dd2f27be1225fdd62fe52dfe6..e2ca067631cf118704c107e2bffbe4ab515abda8 100644 (file)
@@ -1653,6 +1653,7 @@ static int __init doc_probe(unsigned long physadr)
        nand->ecc.mode          = NAND_ECC_HW_SYNDROME;
        nand->ecc.size          = 512;
        nand->ecc.bytes         = 6;
+       nand->ecc.strength      = 2;
        nand->bbt_options       = NAND_BBT_USE_FLASH;
 
        doc->physadr            = physadr;
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c
new file mode 100644 (file)
index 0000000..b082026
--- /dev/null
@@ -0,0 +1,1377 @@
+/*
+ *  Copyright Â© 2012 Mike Dunn <mikedunn@newsguy.com>
+ *
+ * mtd nand driver for M-Systems DiskOnChip G4
+ *
+ * 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.
+ *
+ * Tested on the Palm Treo 680.  The G4 is also present on Toshiba Portege, Asus
+ * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others.
+ * Should work on these as well.  Let me know!
+ *
+ * TODO:
+ *
+ *  Mechanism for management of password-protected areas
+ *
+ *  Hamming ecc when reading oob only
+ *
+ *  According to the M-Sys documentation, this device is also available in a
+ *  "dual-die" configuration having a 256MB capacity, but no mechanism for
+ *  detecting this variant is documented.  Currently this driver assumes 128MB
+ *  capacity.
+ *
+ *  Support for multiple cascaded devices ("floors").  Not sure which gadgets
+ *  contain multiple G4s in a cascaded configuration, if any.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/bch.h>
+#include <linux/bitrev.h>
+
+/*
+ * You'll want to ignore badblocks if you're reading a partition that contains
+ * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since
+ * it does not use mtd nand's method for marking bad blocks (using oob area).
+ * This will also skip the check of the "page written" flag.
+ */
+static bool ignore_badblocks;
+module_param(ignore_badblocks, bool, 0);
+MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed");
+
+struct docg4_priv {
+       struct mtd_info *mtd;
+       struct device *dev;
+       void __iomem *virtadr;
+       int status;
+       struct {
+               unsigned int command;
+               int column;
+               int page;
+       } last_command;
+       uint8_t oob_buf[16];
+       uint8_t ecc_buf[7];
+       int oob_page;
+       struct bch_control *bch;
+};
+
+/*
+ * Defines prefixed with DOCG4 are unique to the diskonchip G4.  All others are
+ * shared with other diskonchip devices (P3, G3 at least).
+ *
+ * Functions with names prefixed with docg4_ are mtd / nand interface functions
+ * (though they may also be called internally).  All others are internal.
+ */
+
+#define DOC_IOSPACE_DATA               0x0800
+
+/* register offsets */
+#define DOC_CHIPID                     0x1000
+#define DOC_DEVICESELECT               0x100a
+#define DOC_ASICMODE                   0x100c
+#define DOC_DATAEND                    0x101e
+#define DOC_NOP                                0x103e
+
+#define DOC_FLASHSEQUENCE              0x1032
+#define DOC_FLASHCOMMAND               0x1034
+#define DOC_FLASHADDRESS               0x1036
+#define DOC_FLASHCONTROL               0x1038
+#define DOC_ECCCONF0                   0x1040
+#define DOC_ECCCONF1                   0x1042
+#define DOC_HAMMINGPARITY              0x1046
+#define DOC_BCH_SYNDROM(idx)           (0x1048 + idx)
+
+#define DOC_ASICMODECONFIRM            0x1072
+#define DOC_CHIPID_INV                 0x1074
+#define DOC_POWERMODE                  0x107c
+
+#define DOCG4_MYSTERY_REG              0x1050
+
+/* apparently used only to write oob bytes 6 and 7 */
+#define DOCG4_OOB_6_7                  0x1052
+
+/* DOC_FLASHSEQUENCE register commands */
+#define DOC_SEQ_RESET                  0x00
+#define DOCG4_SEQ_PAGE_READ            0x03
+#define DOCG4_SEQ_FLUSH                        0x29
+#define DOCG4_SEQ_PAGEWRITE            0x16
+#define DOCG4_SEQ_PAGEPROG             0x1e
+#define DOCG4_SEQ_BLOCKERASE           0x24
+
+/* DOC_FLASHCOMMAND register commands */
+#define DOCG4_CMD_PAGE_READ             0x00
+#define DOC_CMD_ERASECYCLE2            0xd0
+#define DOCG4_CMD_FLUSH                 0x70
+#define DOCG4_CMD_READ2                 0x30
+#define DOC_CMD_PROG_BLOCK_ADDR                0x60
+#define DOCG4_CMD_PAGEWRITE            0x80
+#define DOC_CMD_PROG_CYCLE2            0x10
+#define DOC_CMD_RESET                  0xff
+
+/* DOC_POWERMODE register bits */
+#define DOC_POWERDOWN_READY            0x80
+
+/* DOC_FLASHCONTROL register bits */
+#define DOC_CTRL_CE                    0x10
+#define DOC_CTRL_UNKNOWN               0x40
+#define DOC_CTRL_FLASHREADY            0x01
+
+/* DOC_ECCCONF0 register bits */
+#define DOC_ECCCONF0_READ_MODE         0x8000
+#define DOC_ECCCONF0_UNKNOWN           0x2000
+#define DOC_ECCCONF0_ECC_ENABLE                0x1000
+#define DOC_ECCCONF0_DATA_BYTES_MASK   0x07ff
+
+/* DOC_ECCCONF1 register bits */
+#define DOC_ECCCONF1_BCH_SYNDROM_ERR   0x80
+#define DOC_ECCCONF1_ECC_ENABLE         0x07
+#define DOC_ECCCONF1_PAGE_IS_WRITTEN   0x20
+
+/* DOC_ASICMODE register bits */
+#define DOC_ASICMODE_RESET             0x00
+#define DOC_ASICMODE_NORMAL            0x01
+#define DOC_ASICMODE_POWERDOWN         0x02
+#define DOC_ASICMODE_MDWREN            0x04
+#define DOC_ASICMODE_BDETCT_RESET      0x08
+#define DOC_ASICMODE_RSTIN_RESET       0x10
+#define DOC_ASICMODE_RAM_WE            0x20
+
+/* good status values read after read/write/erase operations */
+#define DOCG4_PROGSTATUS_GOOD          0x51
+#define DOCG4_PROGSTATUS_GOOD_2        0xe0
+
+/*
+ * On read operations (page and oob-only), the first byte read from I/O reg is a
+ * status.  On error, it reads 0x73; otherwise, it reads either 0x71 (first read
+ * after reset only) or 0x51, so bit 1 is presumed to be an error indicator.
+ */
+#define DOCG4_READ_ERROR           0x02 /* bit 1 indicates read error */
+
+/* anatomy of the device */
+#define DOCG4_CHIP_SIZE        0x8000000
+#define DOCG4_PAGE_SIZE        0x200
+#define DOCG4_PAGES_PER_BLOCK  0x200
+#define DOCG4_BLOCK_SIZE       (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE)
+#define DOCG4_NUMBLOCKS        (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE)
+#define DOCG4_OOB_SIZE         0x10
+#define DOCG4_CHIP_SHIFT       27    /* log_2(DOCG4_CHIP_SIZE) */
+#define DOCG4_PAGE_SHIFT       9     /* log_2(DOCG4_PAGE_SIZE) */
+#define DOCG4_ERASE_SHIFT      18    /* log_2(DOCG4_BLOCK_SIZE) */
+
+/* all but the last byte is included in ecc calculation */
+#define DOCG4_BCH_SIZE         (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1)
+
+#define DOCG4_USERDATA_LEN     520 /* 512 byte page plus 8 oob avail to user */
+
+/* expected values from the ID registers */
+#define DOCG4_IDREG1_VALUE     0x0400
+#define DOCG4_IDREG2_VALUE     0xfbff
+
+/* primitive polynomial used to build the Galois field used by hw ecc gen */
+#define DOCG4_PRIMITIVE_POLY   0x4443
+
+#define DOCG4_M                14  /* Galois field is of order 2^14 */
+#define DOCG4_T                4   /* BCH alg corrects up to 4 bit errors */
+
+#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */
+
+/*
+ * Oob bytes 0 - 6 are available to the user.
+ * Byte 7 is hamming ecc for first 7 bytes.  Bytes 8 - 14 are hw-generated ecc.
+ * Byte 15 (the last) is used by the driver as a "page written" flag.
+ */
+static struct nand_ecclayout docg4_oobinfo = {
+       .eccbytes = 9,
+       .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+       .oobavail = 7,
+       .oobfree = { {0, 7} }
+};
+
+/*
+ * The device has a nop register which M-Sys claims is for the purpose of
+ * inserting precise delays.  But beware; at least some operations fail if the
+ * nop writes are replaced with a generic delay!
+ */
+static inline void write_nop(void __iomem *docptr)
+{
+       writew(0, docptr + DOC_NOP);
+}
+
+static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       int i;
+       struct nand_chip *nand = mtd->priv;
+       uint16_t *p = (uint16_t *) buf;
+       len >>= 1;
+
+       for (i = 0; i < len; i++)
+               p[i] = readw(nand->IO_ADDR_R);
+}
+
+static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+       int i;
+       struct nand_chip *nand = mtd->priv;
+       uint16_t *p = (uint16_t *) buf;
+       len >>= 1;
+
+       for (i = 0; i < len; i++)
+               writew(p[i], nand->IO_ADDR_W);
+}
+
+static int poll_status(struct docg4_priv *doc)
+{
+       /*
+        * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL
+        * register.  Operations known to take a long time (e.g., block erase)
+        * should sleep for a while before calling this.
+        */
+
+       uint16_t flash_status;
+       unsigned int timeo;
+       void __iomem *docptr = doc->virtadr;
+
+       dev_dbg(doc->dev, "%s...\n", __func__);
+
+       /* hardware quirk requires reading twice initially */
+       flash_status = readw(docptr + DOC_FLASHCONTROL);
+
+       timeo = 1000;
+       do {
+               cpu_relax();
+               flash_status = readb(docptr + DOC_FLASHCONTROL);
+       } while (!(flash_status & DOC_CTRL_FLASHREADY) && --timeo);
+
+
+       if (!timeo) {
+               dev_err(doc->dev, "%s: timed out!\n", __func__);
+               return NAND_STATUS_FAIL;
+       }
+
+       if (unlikely(timeo < 50))
+               dev_warn(doc->dev, "%s: nearly timed out; %d remaining\n",
+                        __func__, timeo);
+
+       return 0;
+}
+
+
+static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand)
+{
+
+       struct docg4_priv *doc = nand->priv;
+       int status = NAND_STATUS_WP;       /* inverse logic?? */
+       dev_dbg(doc->dev, "%s...\n", __func__);
+
+       /* report any previously unreported error */
+       if (doc->status) {
+               status |= doc->status;
+               doc->status = 0;
+               return status;
+       }
+
+       status |= poll_status(doc);
+       return status;
+}
+
+static void docg4_select_chip(struct mtd_info *mtd, int chip)
+{
+       /*
+        * Select among multiple cascaded chips ("floors").  Multiple floors are
+        * not yet supported, so the only valid non-negative value is 0.
+        */
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+
+       dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip);
+
+       if (chip < 0)
+               return;         /* deselected */
+
+       if (chip > 0)
+               dev_warn(doc->dev, "multiple floors currently unsupported\n");
+
+       writew(0, docptr + DOC_DEVICESELECT);
+}
+
+static void reset(struct mtd_info *mtd)
+{
+       /* full device reset */
+
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+
+       writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN,
+              docptr + DOC_ASICMODE);
+       writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN),
+              docptr + DOC_ASICMODECONFIRM);
+       write_nop(docptr);
+
+       writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN,
+              docptr + DOC_ASICMODE);
+       writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN),
+              docptr + DOC_ASICMODECONFIRM);
+
+       writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1);
+
+       poll_status(doc);
+}
+
+static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf)
+{
+       /* read the 7 hw-generated ecc bytes */
+
+       int i;
+       for (i = 0; i < 7; i++) { /* hw quirk; read twice */
+               ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i));
+               ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i));
+       }
+}
+
+static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page)
+{
+       /*
+        * Called after a page read when hardware reports bitflips.
+        * Up to four bitflips can be corrected.
+        */
+
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i, numerrs, errpos[4];
+       const uint8_t blank_read_hwecc[8] = {
+               0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 };
+
+       read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */
+
+       /* check if read error is due to a blank page */
+       if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7))
+               return 0;       /* yes */
+
+       /* skip additional check of "written flag" if ignore_badblocks */
+       if (ignore_badblocks == false) {
+
+               /*
+                * If the hw ecc bytes are not those of a blank page, there's
+                * still a chance that the page is blank, but was read with
+                * errors.  Check the "written flag" in last oob byte, which
+                * is set to zero when a page is written.  If more than half
+                * the bits are set, assume a blank page.  Unfortunately, the
+                * bit flips(s) are not reported in stats.
+                */
+
+               if (doc->oob_buf[15]) {
+                       int bit, numsetbits = 0;
+                       unsigned long written_flag = doc->oob_buf[15];
+                       for_each_set_bit(bit, &written_flag, 8)
+                               numsetbits++;
+                       if (numsetbits > 4) { /* assume blank */
+                               dev_warn(doc->dev,
+                                        "error(s) in blank page "
+                                        "at offset %08x\n",
+                                        page * DOCG4_PAGE_SIZE);
+                               return 0;
+                       }
+               }
+       }
+
+       /*
+        * The hardware ecc unit produces oob_ecc ^ calc_ecc.  The kernel's bch
+        * algorithm is used to decode this.  However the hw operates on page
+        * data in a bit order that is the reverse of that of the bch alg,
+        * requiring that the bits be reversed on the result.  Thanks to Ivan
+        * Djelic for his analysis!
+        */
+       for (i = 0; i < 7; i++)
+               doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]);
+
+       numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL,
+                            doc->ecc_buf, NULL, errpos);
+
+       if (numerrs == -EBADMSG) {
+               dev_warn(doc->dev, "uncorrectable errors at offset %08x\n",
+                        page * DOCG4_PAGE_SIZE);
+               return -EBADMSG;
+       }
+
+       BUG_ON(numerrs < 0);    /* -EINVAL, or anything other than -EBADMSG */
+
+       /* undo last step in BCH alg (modulo mirroring not needed) */
+       for (i = 0; i < numerrs; i++)
+               errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7));
+
+       /* fix the errors */
+       for (i = 0; i < numerrs; i++) {
+
+               /* ignore if error within oob ecc bytes */
+               if (errpos[i] > DOCG4_USERDATA_LEN * 8)
+                       continue;
+
+               /* if error within oob area preceeding ecc bytes... */
+               if (errpos[i] > DOCG4_PAGE_SIZE * 8)
+                       change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8,
+                                  (unsigned long *)doc->oob_buf);
+
+               else    /* error in page data */
+                       change_bit(errpos[i], (unsigned long *)buf);
+       }
+
+       dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n",
+                  numerrs, page * DOCG4_PAGE_SIZE);
+
+       return numerrs;
+}
+
+static uint8_t docg4_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+
+       dev_dbg(doc->dev, "%s\n", __func__);
+
+       if (doc->last_command.command == NAND_CMD_STATUS) {
+               int status;
+
+               /*
+                * Previous nand command was status request, so nand
+                * infrastructure code expects to read the status here.  If an
+                * error occurred in a previous operation, report it.
+                */
+               doc->last_command.command = 0;
+
+               if (doc->status) {
+                       status = doc->status;
+                       doc->status = 0;
+               }
+
+               /* why is NAND_STATUS_WP inverse logic?? */
+               else
+                       status = NAND_STATUS_WP | NAND_STATUS_READY;
+
+               return status;
+       }
+
+       dev_warn(doc->dev, "unexpectd call to read_byte()\n");
+
+       return 0;
+}
+
+static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr)
+{
+       /* write the four address bytes packed in docg4_addr to the device */
+
+       void __iomem *docptr = doc->virtadr;
+       writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+       docg4_addr >>= 8;
+       writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+       docg4_addr >>= 8;
+       writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+       docg4_addr >>= 8;
+       writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+}
+
+static int read_progstatus(struct docg4_priv *doc)
+{
+       /*
+        * This apparently checks the status of programming.  Done after an
+        * erasure, and after page data is written.  On error, the status is
+        * saved, to be later retrieved by the nand infrastructure code.
+        */
+       void __iomem *docptr = doc->virtadr;
+
+       /* status is read from the I/O reg */
+       uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA);
+       uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA);
+       uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG);
+
+       dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n",
+             __func__, status1, status2, status3);
+
+       if (status1 != DOCG4_PROGSTATUS_GOOD
+           || status2 != DOCG4_PROGSTATUS_GOOD_2
+           || status3 != DOCG4_PROGSTATUS_GOOD_2) {
+               doc->status = NAND_STATUS_FAIL;
+               dev_warn(doc->dev, "read_progstatus failed: "
+                        "%02x, %02x, %02x\n", status1, status2, status3);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int pageprog(struct mtd_info *mtd)
+{
+       /*
+        * Final step in writing a page.  Writes the contents of its
+        * internal buffer out to the flash array, or some such.
+        */
+
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+       int retval = 0;
+
+       dev_dbg(doc->dev, "docg4: %s\n", __func__);
+
+       writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE);
+       writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND);
+       write_nop(docptr);
+       write_nop(docptr);
+
+       /* Just busy-wait; usleep_range() slows things down noticeably. */
+       poll_status(doc);
+
+       writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE);
+       writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND);
+       writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+
+       retval = read_progstatus(doc);
+       writew(0, docptr + DOC_DATAEND);
+       write_nop(docptr);
+       poll_status(doc);
+       write_nop(docptr);
+
+       return retval;
+}
+
+static void sequence_reset(struct mtd_info *mtd)
+{
+       /* common starting sequence for all operations */
+
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+
+       writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL);
+       writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE);
+       writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND);
+       write_nop(docptr);
+       write_nop(docptr);
+       poll_status(doc);
+       write_nop(docptr);
+}
+
+static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
+{
+       /* first step in reading a page */
+
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+
+       dev_dbg(doc->dev,
+             "docg4: %s: g4 page %08x\n", __func__, docg4_addr);
+
+       sequence_reset(mtd);
+
+       writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE);
+       writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND);
+       write_nop(docptr);
+
+       write_addr(doc, docg4_addr);
+
+       write_nop(docptr);
+       writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND);
+       write_nop(docptr);
+       write_nop(docptr);
+
+       poll_status(doc);
+}
+
+static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
+{
+       /* first step in writing a page */
+
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+
+       dev_dbg(doc->dev,
+             "docg4: %s: g4 addr: %x\n", __func__, docg4_addr);
+       sequence_reset(mtd);
+       writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE);
+       writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND);
+       write_nop(docptr);
+       write_addr(doc, docg4_addr);
+       write_nop(docptr);
+       write_nop(docptr);
+       poll_status(doc);
+}
+
+static uint32_t mtd_to_docg4_address(int page, int column)
+{
+       /*
+        * Convert mtd address to format used by the device, 32 bit packed.
+        *
+        * Some notes on G4 addressing... The M-Sys documentation on this device
+        * claims that pages are 2K in length, and indeed, the format of the
+        * address used by the device reflects that.  But within each page are
+        * four 512 byte "sub-pages", each with its own oob data that is
+        * read/written immediately after the 512 bytes of page data.  This oob
+        * data contains the ecc bytes for the preceeding 512 bytes.
+        *
+        * Rather than tell the mtd nand infrastructure that page size is 2k,
+        * with four sub-pages each, we engage in a little subterfuge and tell
+        * the infrastructure code that pages are 512 bytes in size.  This is
+        * done because during the course of reverse-engineering the device, I
+        * never observed an instance where an entire 2K "page" was read or
+        * written as a unit.  Each "sub-page" is always addressed individually,
+        * its data read/written, and ecc handled before the next "sub-page" is
+        * addressed.
+        *
+        * This requires us to convert addresses passed by the mtd nand
+        * infrastructure code to those used by the device.
+        *
+        * The address that is written to the device consists of four bytes: the
+        * first two are the 2k page number, and the second is the index into
+        * the page.  The index is in terms of 16-bit half-words and includes
+        * the preceeding oob data, so e.g., the index into the second
+        * "sub-page" is 0x108, and the full device address of the start of mtd
+        * page 0x201 is 0x00800108.
+        */
+       int g4_page = page / 4;                       /* device's 2K page */
+       int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */
+       return (g4_page << 16) | g4_index;            /* pack */
+}
+
+static void docg4_command(struct mtd_info *mtd, unsigned command, int column,
+                         int page_addr)
+{
+       /* handle standard nand commands */
+
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       uint32_t g4_addr = mtd_to_docg4_address(page_addr, column);
+
+       dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n",
+             __func__, command, page_addr, column);
+
+       /*
+        * Save the command and its arguments.  This enables emulation of
+        * standard flash devices, and also some optimizations.
+        */
+       doc->last_command.command = command;
+       doc->last_command.column = column;
+       doc->last_command.page = page_addr;
+
+       switch (command) {
+
+       case NAND_CMD_RESET:
+               reset(mtd);
+               break;
+
+       case NAND_CMD_READ0:
+               read_page_prologue(mtd, g4_addr);
+               break;
+
+       case NAND_CMD_STATUS:
+               /* next call to read_byte() will expect a status */
+               break;
+
+       case NAND_CMD_SEQIN:
+               write_page_prologue(mtd, g4_addr);
+
+               /* hack for deferred write of oob bytes */
+               if (doc->oob_page == page_addr)
+                       memcpy(nand->oob_poi, doc->oob_buf, 16);
+               break;
+
+       case NAND_CMD_PAGEPROG:
+               pageprog(mtd);
+               break;
+
+       /* we don't expect these, based on review of nand_base.c */
+       case NAND_CMD_READOOB:
+       case NAND_CMD_READID:
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+               dev_warn(doc->dev, "docg4_command: "
+                        "unexpected nand command 0x%x\n", command);
+               break;
+
+       }
+}
+
+static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
+                    uint8_t *buf, int page, bool use_ecc)
+{
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+       uint16_t status, edc_err, *buf16;
+
+       dev_dbg(doc->dev, "%s: page %08x\n", __func__, page);
+
+       writew(DOC_ECCCONF0_READ_MODE |
+              DOC_ECCCONF0_ECC_ENABLE |
+              DOC_ECCCONF0_UNKNOWN |
+              DOCG4_BCH_SIZE,
+              docptr + DOC_ECCCONF0);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+
+       /* the 1st byte from the I/O reg is a status; the rest is page data */
+       status = readw(docptr + DOC_IOSPACE_DATA);
+       if (status & DOCG4_READ_ERROR) {
+               dev_err(doc->dev,
+                       "docg4_read_page: bad status: 0x%02x\n", status);
+               writew(0, docptr + DOC_DATAEND);
+               return -EIO;
+       }
+
+       dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status);
+
+       docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */
+
+       /*
+        * Diskonchips read oob immediately after a page read.  Mtd
+        * infrastructure issues a separate command for reading oob after the
+        * page is read.  So we save the oob bytes in a local buffer and just
+        * copy it if the next command reads oob from the same page.
+        */
+
+       /* first 14 oob bytes read from I/O reg */
+       docg4_read_buf(mtd, doc->oob_buf, 14);
+
+       /* last 2 read from another reg */
+       buf16 = (uint16_t *)(doc->oob_buf + 14);
+       *buf16 = readw(docptr + DOCG4_MYSTERY_REG);
+
+       write_nop(docptr);
+
+       if (likely(use_ecc == true)) {
+
+               /* read the register that tells us if bitflip(s) detected  */
+               edc_err = readw(docptr + DOC_ECCCONF1);
+               edc_err = readw(docptr + DOC_ECCCONF1);
+               dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err);
+
+               /* If bitflips are reported, attempt to correct with ecc */
+               if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) {
+                       int bits_corrected = correct_data(mtd, buf, page);
+                       if (bits_corrected == -EBADMSG)
+                               mtd->ecc_stats.failed++;
+                       else
+                               mtd->ecc_stats.corrected += bits_corrected;
+               }
+       }
+
+       writew(0, docptr + DOC_DATAEND);
+       return 0;
+}
+
+
+static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
+                              uint8_t *buf, int page)
+{
+       return read_page(mtd, nand, buf, page, false);
+}
+
+static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand,
+                          uint8_t *buf, int page)
+{
+       return read_page(mtd, nand, buf, page, true);
+}
+
+static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
+                         int page, int sndcmd)
+{
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+       uint16_t status;
+
+       dev_dbg(doc->dev, "%s: page %x\n", __func__, page);
+
+       /*
+        * Oob bytes are read as part of a normal page read.  If the previous
+        * nand command was a read of the page whose oob is now being read, just
+        * copy the oob bytes that we saved in a local buffer and avoid a
+        * separate oob read.
+        */
+       if (doc->last_command.command == NAND_CMD_READ0 &&
+           doc->last_command.page == page) {
+               memcpy(nand->oob_poi, doc->oob_buf, 16);
+               return 0;
+       }
+
+       /*
+        * Separate read of oob data only.
+        */
+       docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page);
+
+       writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+
+       /* the 1st byte from the I/O reg is a status; the rest is oob data */
+       status = readw(docptr + DOC_IOSPACE_DATA);
+       if (status & DOCG4_READ_ERROR) {
+               dev_warn(doc->dev,
+                        "docg4_read_oob failed: status = 0x%02x\n", status);
+               return -EIO;
+       }
+
+       dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status);
+
+       docg4_read_buf(mtd, nand->oob_poi, 16);
+
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+       writew(0, docptr + DOC_DATAEND);
+       write_nop(docptr);
+
+       return 0;
+}
+
+static void docg4_erase_block(struct mtd_info *mtd, int page)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+       uint16_t g4_page;
+
+       dev_dbg(doc->dev, "%s: page %04x\n", __func__, page);
+
+       sequence_reset(mtd);
+
+       writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE);
+       writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND);
+       write_nop(docptr);
+
+       /* only 2 bytes of address are written to specify erase block */
+       g4_page = (uint16_t)(page / 4);  /* to g4's 2k page addressing */
+       writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS);
+       g4_page >>= 8;
+       writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS);
+       write_nop(docptr);
+
+       /* start the erasure */
+       writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND);
+       write_nop(docptr);
+       write_nop(docptr);
+
+       usleep_range(500, 1000); /* erasure is long; take a snooze */
+       poll_status(doc);
+       writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE);
+       writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND);
+       writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+       write_nop(docptr);
+
+       read_progstatus(doc);
+
+       writew(0, docptr + DOC_DATAEND);
+       write_nop(docptr);
+       poll_status(doc);
+       write_nop(docptr);
+}
+
+static void write_page(struct mtd_info *mtd, struct nand_chip *nand,
+                      const uint8_t *buf, bool use_ecc)
+{
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+       uint8_t ecc_buf[8];
+
+       dev_dbg(doc->dev, "%s...\n", __func__);
+
+       writew(DOC_ECCCONF0_ECC_ENABLE |
+              DOC_ECCCONF0_UNKNOWN |
+              DOCG4_BCH_SIZE,
+              docptr + DOC_ECCCONF0);
+       write_nop(docptr);
+
+       /* write the page data */
+       docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE);
+
+       /* oob bytes 0 through 5 are written to I/O reg */
+       docg4_write_buf16(mtd, nand->oob_poi, 6);
+
+       /* oob byte 6 written to a separate reg */
+       writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7);
+
+       write_nop(docptr);
+       write_nop(docptr);
+
+       /* write hw-generated ecc bytes to oob */
+       if (likely(use_ecc == true)) {
+               /* oob byte 7 is hamming code */
+               uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY);
+               hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */
+               writew(hamming, docptr + DOCG4_OOB_6_7);
+               write_nop(docptr);
+
+               /* read the 7 bch bytes from ecc regs */
+               read_hw_ecc(docptr, ecc_buf);
+               ecc_buf[7] = 0;         /* clear the "page written" flag */
+       }
+
+       /* write user-supplied bytes to oob */
+       else {
+               writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7);
+               write_nop(docptr);
+               memcpy(ecc_buf, &nand->oob_poi[8], 8);
+       }
+
+       docg4_write_buf16(mtd, ecc_buf, 8);
+       write_nop(docptr);
+       write_nop(docptr);
+       writew(0, docptr + DOC_DATAEND);
+       write_nop(docptr);
+}
+
+static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
+                                const uint8_t *buf)
+{
+       return write_page(mtd, nand, buf, false);
+}
+
+static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
+                            const uint8_t *buf)
+{
+       return write_page(mtd, nand, buf, true);
+}
+
+static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand,
+                          int page)
+{
+       /*
+        * Writing oob-only is not really supported, because MLC nand must write
+        * oob bytes at the same time as page data.  Nonetheless, we save the
+        * oob buffer contents here, and then write it along with the page data
+        * if the same page is subsequently written.  This allows user space
+        * utilities that write the oob data prior to the page data to work
+        * (e.g., nandwrite).  The disdvantage is that, if the intention was to
+        * write oob only, the operation is quietly ignored.  Also, oob can get
+        * corrupted if two concurrent processes are running nandwrite.
+        */
+
+       /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */
+       struct docg4_priv *doc = nand->priv;
+       doc->oob_page = page;
+       memcpy(doc->oob_buf, nand->oob_poi, 16);
+       return 0;
+}
+
+static int __init read_factory_bbt(struct mtd_info *mtd)
+{
+       /*
+        * The device contains a read-only factory bad block table.  Read it and
+        * update the memory-based bbt accordingly.
+        */
+
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0);
+       uint8_t *buf;
+       int i, block, status;
+
+       buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       read_page_prologue(mtd, g4_addr);
+       status = docg4_read_page(mtd, nand, buf, DOCG4_FACTORY_BBT_PAGE);
+       if (status)
+               goto exit;
+
+       /*
+        * If no memory-based bbt was created, exit.  This will happen if module
+        * parameter ignore_badblocks is set.  Then why even call this function?
+        * For an unknown reason, block erase always fails if it's the first
+        * operation after device power-up.  The above read ensures it never is.
+        * Ugly, I know.
+        */
+       if (nand->bbt == NULL)  /* no memory-based bbt */
+               goto exit;
+
+       /*
+        * Parse factory bbt and update memory-based bbt.  Factory bbt format is
+        * simple: one bit per block, block numbers increase left to right (msb
+        * to lsb).  Bit clear means bad block.
+        */
+       for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) {
+               int bitnum;
+               unsigned long bits = ~buf[i];
+               for_each_set_bit(bitnum, &bits, 8) {
+                       int badblock = block + 7 - bitnum;
+                       nand->bbt[badblock / 4] |=
+                               0x03 << ((badblock % 4) * 2);
+                       mtd->ecc_stats.badblocks++;
+                       dev_notice(doc->dev, "factory-marked bad block: %d\n",
+                                  badblock);
+               }
+       }
+ exit:
+       kfree(buf);
+       return status;
+}
+
+static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+       /*
+        * Mark a block as bad.  Bad blocks are marked in the oob area of the
+        * first page of the block.  The default scan_bbt() in the nand
+        * infrastructure code works fine for building the memory-based bbt
+        * during initialization, as does the nand infrastructure function that
+        * checks if a block is bad by reading the bbt.  This function replaces
+        * the nand default because writes to oob-only are not supported.
+        */
+
+       int ret, i;
+       uint8_t *buf;
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       struct nand_bbt_descr *bbtd = nand->badblock_pattern;
+       int block = (int)(ofs >> nand->bbt_erase_shift);
+       int page = (int)(ofs >> nand->page_shift);
+       uint32_t g4_addr = mtd_to_docg4_address(page, 0);
+
+       dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs);
+
+       if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1)))
+               dev_warn(doc->dev, "%s: ofs %llx not start of block!\n",
+                        __func__, ofs);
+
+       /* allocate blank buffer for page data */
+       buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       /* update bbt in memory */
+       nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2);
+
+       /* write bit-wise negation of pattern to oob buffer */
+       memset(nand->oob_poi, 0xff, mtd->oobsize);
+       for (i = 0; i < bbtd->len; i++)
+               nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i];
+
+       /* write first page of block */
+       write_page_prologue(mtd, g4_addr);
+       docg4_write_page(mtd, nand, buf);
+       ret = pageprog(mtd);
+       if (!ret)
+               mtd->ecc_stats.badblocks++;
+
+       kfree(buf);
+
+       return ret;
+}
+
+static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+       /* only called when module_param ignore_badblocks is set */
+       return 0;
+}
+
+static int docg4_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       /*
+        * Put the device into "deep power-down" mode.  Note that CE# must be
+        * deasserted for this to take effect.  The xscale, e.g., can be
+        * configured to float this signal when the processor enters power-down,
+        * and a suitable pull-up ensures its deassertion.
+        */
+
+       int i;
+       uint8_t pwr_down;
+       struct docg4_priv *doc = platform_get_drvdata(pdev);
+       void __iomem *docptr = doc->virtadr;
+
+       dev_dbg(doc->dev, "%s...\n", __func__);
+
+       /* poll the register that tells us we're ready to go to sleep */
+       for (i = 0; i < 10; i++) {
+               pwr_down = readb(docptr + DOC_POWERMODE);
+               if (pwr_down & DOC_POWERDOWN_READY)
+                       break;
+               usleep_range(1000, 4000);
+       }
+
+       if (pwr_down & DOC_POWERDOWN_READY) {
+               dev_err(doc->dev, "suspend failed; "
+                       "timeout polling DOC_POWERDOWN_READY\n");
+               return -EIO;
+       }
+
+       writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN,
+              docptr + DOC_ASICMODE);
+       writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN),
+              docptr + DOC_ASICMODECONFIRM);
+
+       write_nop(docptr);
+
+       return 0;
+}
+
+static int docg4_resume(struct platform_device *pdev)
+{
+
+       /*
+        * Exit power-down.  Twelve consecutive reads of the address below
+        * accomplishes this, assuming CE# has been asserted.
+        */
+
+       struct docg4_priv *doc = platform_get_drvdata(pdev);
+       void __iomem *docptr = doc->virtadr;
+       int i;
+
+       dev_dbg(doc->dev, "%s...\n", __func__);
+
+       for (i = 0; i < 12; i++)
+               readb(docptr + 0x1fff);
+
+       return 0;
+}
+
+static void __init init_mtd_structs(struct mtd_info *mtd)
+{
+       /* initialize mtd and nand data structures */
+
+       /*
+        * Note that some of the following initializations are not usually
+        * required within a nand driver because they are performed by the nand
+        * infrastructure code as part of nand_scan().  In this case they need
+        * to be initialized here because we skip call to nand_scan_ident() (the
+        * first half of nand_scan()).  The call to nand_scan_ident() is skipped
+        * because for this device the chip id is not read in the manner of a
+        * standard nand device.  Unfortunately, nand_scan_ident() does other
+        * things as well, such as call nand_set_defaults().
+        */
+
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+
+       mtd->size = DOCG4_CHIP_SIZE;
+       mtd->name = "Msys_Diskonchip_G4";
+       mtd->writesize = DOCG4_PAGE_SIZE;
+       mtd->erasesize = DOCG4_BLOCK_SIZE;
+       mtd->oobsize = DOCG4_OOB_SIZE;
+       nand->chipsize = DOCG4_CHIP_SIZE;
+       nand->chip_shift = DOCG4_CHIP_SHIFT;
+       nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT;
+       nand->chip_delay = 20;
+       nand->page_shift = DOCG4_PAGE_SHIFT;
+       nand->pagemask = 0x3ffff;
+       nand->badblockpos = NAND_LARGE_BADBLOCK_POS;
+       nand->badblockbits = 8;
+       nand->ecc.layout = &docg4_oobinfo;
+       nand->ecc.mode = NAND_ECC_HW_SYNDROME;
+       nand->ecc.size = DOCG4_PAGE_SIZE;
+       nand->ecc.prepad = 8;
+       nand->ecc.bytes = 8;
+       nand->ecc.strength = DOCG4_T;
+       nand->options =
+               NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR;
+       nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA;
+       nand->controller = &nand->hwcontrol;
+       spin_lock_init(&nand->controller->lock);
+       init_waitqueue_head(&nand->controller->wq);
+
+       /* methods */
+       nand->cmdfunc = docg4_command;
+       nand->waitfunc = docg4_wait;
+       nand->select_chip = docg4_select_chip;
+       nand->read_byte = docg4_read_byte;
+       nand->block_markbad = docg4_block_markbad;
+       nand->read_buf = docg4_read_buf;
+       nand->write_buf = docg4_write_buf16;
+       nand->scan_bbt = nand_default_bbt;
+       nand->erase_cmd = docg4_erase_block;
+       nand->ecc.read_page = docg4_read_page;
+       nand->ecc.write_page = docg4_write_page;
+       nand->ecc.read_page_raw = docg4_read_page_raw;
+       nand->ecc.write_page_raw = docg4_write_page_raw;
+       nand->ecc.read_oob = docg4_read_oob;
+       nand->ecc.write_oob = docg4_write_oob;
+
+       /*
+        * The way the nand infrastructure code is written, a memory-based bbt
+        * is not created if NAND_SKIP_BBTSCAN is set.  With no memory bbt,
+        * nand->block_bad() is used.  So when ignoring bad blocks, we skip the
+        * scan and define a dummy block_bad() which always returns 0.
+        */
+       if (ignore_badblocks) {
+               nand->options |= NAND_SKIP_BBTSCAN;
+               nand->block_bad = docg4_block_neverbad;
+       }
+
+}
+
+static int __init read_id_reg(struct mtd_info *mtd)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct docg4_priv *doc = nand->priv;
+       void __iomem *docptr = doc->virtadr;
+       uint16_t id1, id2;
+
+       /* check for presence of g4 chip by reading id registers */
+       id1 = readw(docptr + DOC_CHIPID);
+       id1 = readw(docptr + DOCG4_MYSTERY_REG);
+       id2 = readw(docptr + DOC_CHIPID_INV);
+       id2 = readw(docptr + DOCG4_MYSTERY_REG);
+
+       if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) {
+               dev_info(doc->dev,
+                        "NAND device: 128MiB Diskonchip G4 detected\n");
+               return 0;
+       }
+
+       return -ENODEV;
+}
+
+static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL };
+
+static int __init probe_docg4(struct platform_device *pdev)
+{
+       struct mtd_info *mtd;
+       struct nand_chip *nand;
+       void __iomem *virtadr;
+       struct docg4_priv *doc;
+       int len, retval;
+       struct resource *r;
+       struct device *dev = &pdev->dev;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (r == NULL) {
+               dev_err(dev, "no io memory resource defined!\n");
+               return -ENODEV;
+       }
+
+       virtadr = ioremap(r->start, resource_size(r));
+       if (!virtadr) {
+               dev_err(dev, "Diskonchip ioremap failed: %pR\n", r);
+               return -EIO;
+       }
+
+       len = sizeof(struct mtd_info) + sizeof(struct nand_chip) +
+               sizeof(struct docg4_priv);
+       mtd = kzalloc(len, GFP_KERNEL);
+       if (mtd == NULL) {
+               retval = -ENOMEM;
+               goto fail;
+       }
+       nand = (struct nand_chip *) (mtd + 1);
+       doc = (struct docg4_priv *) (nand + 1);
+       mtd->priv = nand;
+       nand->priv = doc;
+       mtd->owner = THIS_MODULE;
+       doc->virtadr = virtadr;
+       doc->dev = dev;
+
+       init_mtd_structs(mtd);
+
+       /* initialize kernel bch algorithm */
+       doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY);
+       if (doc->bch == NULL) {
+               retval = -EINVAL;
+               goto fail;
+       }
+
+       platform_set_drvdata(pdev, doc);
+
+       reset(mtd);
+       retval = read_id_reg(mtd);
+       if (retval == -ENODEV) {
+               dev_warn(dev, "No diskonchip G4 device found.\n");
+               goto fail;
+       }
+
+       retval = nand_scan_tail(mtd);
+       if (retval)
+               goto fail;
+
+       retval = read_factory_bbt(mtd);
+       if (retval)
+               goto fail;
+
+       retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
+       if (retval)
+               goto fail;
+
+       doc->mtd = mtd;
+       return 0;
+
+ fail:
+       iounmap(virtadr);
+       if (mtd) {
+               /* re-declarations avoid compiler warning */
+               struct nand_chip *nand = mtd->priv;
+               struct docg4_priv *doc = nand->priv;
+               nand_release(mtd); /* deletes partitions and mtd devices */
+               platform_set_drvdata(pdev, NULL);
+               free_bch(doc->bch);
+               kfree(mtd);
+       }
+
+       return retval;
+}
+
+static int __exit cleanup_docg4(struct platform_device *pdev)
+{
+       struct docg4_priv *doc = platform_get_drvdata(pdev);
+       nand_release(doc->mtd);
+       platform_set_drvdata(pdev, NULL);
+       free_bch(doc->bch);
+       kfree(doc->mtd);
+       iounmap(doc->virtadr);
+       return 0;
+}
+
+static struct platform_driver docg4_driver = {
+       .driver         = {
+               .name   = "docg4",
+               .owner  = THIS_MODULE,
+       },
+       .suspend        = docg4_suspend,
+       .resume         = docg4_resume,
+       .remove         = __exit_p(cleanup_docg4),
+};
+
+static int __init docg4_init(void)
+{
+       return platform_driver_probe(&docg4_driver, probe_docg4);
+}
+
+static void __exit docg4_exit(void)
+{
+       platform_driver_unregister(&docg4_driver);
+}
+
+module_init(docg4_init);
+module_exit(docg4_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Dunn");
+MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver");
index 7195ee6efe12293df9e92302959dbe786b3f80bd..80b5264f0a32f031a10f5b50e50987a1184646e1 100644 (file)
@@ -813,6 +813,12 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
                                &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0;
                chip->ecc.size = 512;
                chip->ecc.bytes = 3;
+               chip->ecc.strength = 1;
+               /*
+                * FIXME: can hardware ecc correct 4 bitflips if page size is
+                * 2k?  Then does hardware report number of corrections for this
+                * case?  If so, ecc_stats reporting needs to be fixed as well.
+                */
        } else {
                /* otherwise fall back to default software ECC */
                chip->ecc.mode = NAND_ECC_SOFT;
index e53b76064133fc52bded214fb57a94696abcb891..1b8330e1155a4468f4fd272a052b9b095850eddb 100644 (file)
  */
 
 #include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -27,6 +31,7 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand_ecc.h>
 #include <linux/platform_device.h>
+#include <linux/of.h>
 #include <linux/mtd/partitions.h>
 #include <linux/io.h>
 #include <linux/slab.h>
@@ -34,7 +39,7 @@
 #include <linux/amba/bus.h>
 #include <mtd/mtd-abi.h>
 
-static struct nand_ecclayout fsmc_ecc1_layout = {
+static struct nand_ecclayout fsmc_ecc1_128_layout = {
        .eccbytes = 24,
        .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52,
                66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116},
@@ -50,7 +55,127 @@ static struct nand_ecclayout fsmc_ecc1_layout = {
        }
 };
 
-static struct nand_ecclayout fsmc_ecc4_lp_layout = {
+static struct nand_ecclayout fsmc_ecc1_64_layout = {
+       .eccbytes = 12,
+       .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52},
+       .oobfree = {
+               {.offset = 8, .length = 8},
+               {.offset = 24, .length = 8},
+               {.offset = 40, .length = 8},
+               {.offset = 56, .length = 8},
+       }
+};
+
+static struct nand_ecclayout fsmc_ecc1_16_layout = {
+       .eccbytes = 3,
+       .eccpos = {2, 3, 4},
+       .oobfree = {
+               {.offset = 8, .length = 8},
+       }
+};
+
+/*
+ * ECC4 layout for NAND of pagesize 8192 bytes & OOBsize 256 bytes. 13*16 bytes
+ * of OB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 46
+ * bytes are free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_256_layout = {
+       .eccbytes = 208,
+       .eccpos = {  2,   3,   4,   5,   6,   7,   8,
+               9,  10,  11,  12,  13,  14,
+               18,  19,  20,  21,  22,  23,  24,
+               25,  26,  27,  28,  29,  30,
+               34,  35,  36,  37,  38,  39,  40,
+               41,  42,  43,  44,  45,  46,
+               50,  51,  52,  53,  54,  55,  56,
+               57,  58,  59,  60,  61,  62,
+               66,  67,  68,  69,  70,  71,  72,
+               73,  74,  75,  76,  77,  78,
+               82,  83,  84,  85,  86,  87,  88,
+               89,  90,  91,  92,  93,  94,
+               98,  99, 100, 101, 102, 103, 104,
+               105, 106, 107, 108, 109, 110,
+               114, 115, 116, 117, 118, 119, 120,
+               121, 122, 123, 124, 125, 126,
+               130, 131, 132, 133, 134, 135, 136,
+               137, 138, 139, 140, 141, 142,
+               146, 147, 148, 149, 150, 151, 152,
+               153, 154, 155, 156, 157, 158,
+               162, 163, 164, 165, 166, 167, 168,
+               169, 170, 171, 172, 173, 174,
+               178, 179, 180, 181, 182, 183, 184,
+               185, 186, 187, 188, 189, 190,
+               194, 195, 196, 197, 198, 199, 200,
+               201, 202, 203, 204, 205, 206,
+               210, 211, 212, 213, 214, 215, 216,
+               217, 218, 219, 220, 221, 222,
+               226, 227, 228, 229, 230, 231, 232,
+               233, 234, 235, 236, 237, 238,
+               242, 243, 244, 245, 246, 247, 248,
+               249, 250, 251, 252, 253, 254
+       },
+       .oobfree = {
+               {.offset = 15, .length = 3},
+               {.offset = 31, .length = 3},
+               {.offset = 47, .length = 3},
+               {.offset = 63, .length = 3},
+               {.offset = 79, .length = 3},
+               {.offset = 95, .length = 3},
+               {.offset = 111, .length = 3},
+               {.offset = 127, .length = 3},
+               {.offset = 143, .length = 3},
+               {.offset = 159, .length = 3},
+               {.offset = 175, .length = 3},
+               {.offset = 191, .length = 3},
+               {.offset = 207, .length = 3},
+               {.offset = 223, .length = 3},
+               {.offset = 239, .length = 3},
+               {.offset = 255, .length = 1}
+       }
+};
+
+/*
+ * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes
+ * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118
+ * bytes are free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_224_layout = {
+       .eccbytes = 104,
+       .eccpos = {  2,   3,   4,   5,   6,   7,   8,
+               9,  10,  11,  12,  13,  14,
+               18,  19,  20,  21,  22,  23,  24,
+               25,  26,  27,  28,  29,  30,
+               34,  35,  36,  37,  38,  39,  40,
+               41,  42,  43,  44,  45,  46,
+               50,  51,  52,  53,  54,  55,  56,
+               57,  58,  59,  60,  61,  62,
+               66,  67,  68,  69,  70,  71,  72,
+               73,  74,  75,  76,  77,  78,
+               82,  83,  84,  85,  86,  87,  88,
+               89,  90,  91,  92,  93,  94,
+               98,  99, 100, 101, 102, 103, 104,
+               105, 106, 107, 108, 109, 110,
+               114, 115, 116, 117, 118, 119, 120,
+               121, 122, 123, 124, 125, 126
+       },
+       .oobfree = {
+               {.offset = 15, .length = 3},
+               {.offset = 31, .length = 3},
+               {.offset = 47, .length = 3},
+               {.offset = 63, .length = 3},
+               {.offset = 79, .length = 3},
+               {.offset = 95, .length = 3},
+               {.offset = 111, .length = 3},
+               {.offset = 127, .length = 97}
+       }
+};
+
+/*
+ * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 128 bytes. 13*8 bytes
+ * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 22
+ * bytes are free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_128_layout = {
        .eccbytes = 104,
        .eccpos = {  2,   3,   4,   5,   6,   7,   8,
                9,  10,  11,  12,  13,  14,
@@ -81,6 +206,45 @@ static struct nand_ecclayout fsmc_ecc4_lp_layout = {
        }
 };
 
+/*
+ * ECC4 layout for NAND of pagesize 2048 bytes & OOBsize 64 bytes. 13*4 bytes of
+ * OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 10
+ * bytes are free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_64_layout = {
+       .eccbytes = 52,
+       .eccpos = {  2,   3,   4,   5,   6,   7,   8,
+               9,  10,  11,  12,  13,  14,
+               18,  19,  20,  21,  22,  23,  24,
+               25,  26,  27,  28,  29,  30,
+               34,  35,  36,  37,  38,  39,  40,
+               41,  42,  43,  44,  45,  46,
+               50,  51,  52,  53,  54,  55,  56,
+               57,  58,  59,  60,  61,  62,
+       },
+       .oobfree = {
+               {.offset = 15, .length = 3},
+               {.offset = 31, .length = 3},
+               {.offset = 47, .length = 3},
+               {.offset = 63, .length = 1},
+       }
+};
+
+/*
+ * ECC4 layout for NAND of pagesize 512 bytes & OOBsize 16 bytes. 13 bytes of
+ * OOB size is reserved for ECC, Byte no. 4 & 5 reserved for bad block and One
+ * byte is free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_16_layout = {
+       .eccbytes = 13,
+       .eccpos = { 0,  1,  2,  3,  6,  7, 8,
+               9, 10, 11, 12, 13, 14
+       },
+       .oobfree = {
+               {.offset = 15, .length = 1},
+       }
+};
+
 /*
  * ECC placement definitions in oobfree type format.
  * There are 13 bytes of ecc for every 512 byte block and it has to be read
@@ -103,16 +267,6 @@ static struct fsmc_eccplace fsmc_ecc4_lp_place = {
        }
 };
 
-static struct nand_ecclayout fsmc_ecc4_sp_layout = {
-       .eccbytes = 13,
-       .eccpos = { 0,  1,  2,  3,  6,  7, 8,
-               9, 10, 11, 12, 13, 14
-       },
-       .oobfree = {
-               {.offset = 15, .length = 1},
-       }
-};
-
 static struct fsmc_eccplace fsmc_ecc4_sp_place = {
        .eccplace = {
                {.offset = 0, .length = 4},
@@ -120,75 +274,24 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = {
        }
 };
 
-/*
- * Default partition tables to be used if the partition information not
- * provided through platform data.
- *
- * Default partition layout for small page(= 512 bytes) devices
- * Size for "Root file system" is updated in driver based on actual device size
- */
-static struct mtd_partition partition_info_16KB_blk[] = {
-       {
-               .name = "X-loader",
-               .offset = 0,
-               .size = 4*0x4000,
-       },
-       {
-               .name = "U-Boot",
-               .offset = 0x10000,
-               .size = 20*0x4000,
-       },
-       {
-               .name = "Kernel",
-               .offset = 0x60000,
-               .size = 256*0x4000,
-       },
-       {
-               .name = "Root File System",
-               .offset = 0x460000,
-               .size = MTDPART_SIZ_FULL,
-       },
-};
-
-/*
- * Default partition layout for large page(> 512 bytes) devices
- * Size for "Root file system" is updated in driver based on actual device size
- */
-static struct mtd_partition partition_info_128KB_blk[] = {
-       {
-               .name = "X-loader",
-               .offset = 0,
-               .size = 4*0x20000,
-       },
-       {
-               .name = "U-Boot",
-               .offset = 0x80000,
-               .size = 12*0x20000,
-       },
-       {
-               .name = "Kernel",
-               .offset = 0x200000,
-               .size = 48*0x20000,
-       },
-       {
-               .name = "Root File System",
-               .offset = 0x800000,
-               .size = MTDPART_SIZ_FULL,
-       },
-};
-
-
 /**
  * struct fsmc_nand_data - structure for FSMC NAND device state
  *
  * @pid:               Part ID on the AMBA PrimeCell format
  * @mtd:               MTD info for a NAND flash.
  * @nand:              Chip related info for a NAND flash.
+ * @partitions:                Partition info for a NAND Flash.
+ * @nr_partitions:     Total number of partition of a NAND flash.
  *
  * @ecc_place:         ECC placing locations in oobfree type format.
  * @bank:              Bank number for probed device.
  * @clk:               Clock structure for FSMC.
  *
+ * @read_dma_chan:     DMA channel for read access
+ * @write_dma_chan:    DMA channel for write access to NAND
+ * @dma_access_complete: Completion structure
+ *
+ * @data_pa:           NAND Physical port for Data.
  * @data_va:           NAND port for Data.
  * @cmd_va:            NAND port for Command.
  * @addr_va:           NAND port for Address.
@@ -198,16 +301,23 @@ struct fsmc_nand_data {
        u32                     pid;
        struct mtd_info         mtd;
        struct nand_chip        nand;
+       struct mtd_partition    *partitions;
+       unsigned int            nr_partitions;
 
        struct fsmc_eccplace    *ecc_place;
        unsigned int            bank;
+       struct device           *dev;
+       enum access_mode        mode;
        struct clk              *clk;
 
-       struct resource         *resregs;
-       struct resource         *rescmd;
-       struct resource         *resaddr;
-       struct resource         *resdata;
+       /* DMA related objects */
+       struct dma_chan         *read_dma_chan;
+       struct dma_chan         *write_dma_chan;
+       struct completion       dma_access_complete;
+
+       struct fsmc_nand_timings *dev_timings;
 
+       dma_addr_t              data_pa;
        void __iomem            *data_va;
        void __iomem            *cmd_va;
        void __iomem            *addr_va;
@@ -251,28 +361,29 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
        struct nand_chip *this = mtd->priv;
        struct fsmc_nand_data *host = container_of(mtd,
                                        struct fsmc_nand_data, mtd);
-       struct fsmc_regs *regs = host->regs_va;
+       void *__iomem *regs = host->regs_va;
        unsigned int bank = host->bank;
 
        if (ctrl & NAND_CTRL_CHANGE) {
+               u32 pc;
+
                if (ctrl & NAND_CLE) {
-                       this->IO_ADDR_R = (void __iomem *)host->cmd_va;
-                       this->IO_ADDR_W = (void __iomem *)host->cmd_va;
+                       this->IO_ADDR_R = host->cmd_va;
+                       this->IO_ADDR_W = host->cmd_va;
                } else if (ctrl & NAND_ALE) {
-                       this->IO_ADDR_R = (void __iomem *)host->addr_va;
-                       this->IO_ADDR_W = (void __iomem *)host->addr_va;
+                       this->IO_ADDR_R = host->addr_va;
+                       this->IO_ADDR_W = host->addr_va;
                } else {
-                       this->IO_ADDR_R = (void __iomem *)host->data_va;
-                       this->IO_ADDR_W = (void __iomem *)host->data_va;
+                       this->IO_ADDR_R = host->data_va;
+                       this->IO_ADDR_W = host->data_va;
                }
 
-               if (ctrl & NAND_NCE) {
-                       writel(readl(&regs->bank_regs[bank].pc) | FSMC_ENABLE,
-                                       &regs->bank_regs[bank].pc);
-               } else {
-                       writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ENABLE,
-                                      &regs->bank_regs[bank].pc);
-               }
+               pc = readl(FSMC_NAND_REG(regs, bank, PC));
+               if (ctrl & NAND_NCE)
+                       pc |= FSMC_ENABLE;
+               else
+                       pc &= ~FSMC_ENABLE;
+               writel(pc, FSMC_NAND_REG(regs, bank, PC));
        }
 
        mb();
@@ -287,22 +398,42 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
  * This routine initializes timing parameters related to NAND memory access in
  * FSMC registers
  */
-static void __init fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank,
-                                  uint32_t busw)
+static void fsmc_nand_setup(void __iomem *regs, uint32_t bank,
+                          uint32_t busw, struct fsmc_nand_timings *timings)
 {
        uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
+       uint32_t tclr, tar, thiz, thold, twait, tset;
+       struct fsmc_nand_timings *tims;
+       struct fsmc_nand_timings default_timings = {
+               .tclr   = FSMC_TCLR_1,
+               .tar    = FSMC_TAR_1,
+               .thiz   = FSMC_THIZ_1,
+               .thold  = FSMC_THOLD_4,
+               .twait  = FSMC_TWAIT_6,
+               .tset   = FSMC_TSET_0,
+       };
+
+       if (timings)
+               tims = timings;
+       else
+               tims = &default_timings;
+
+       tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
+       tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
+       thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT;
+       thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT;
+       twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT;
+       tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
 
        if (busw)
-               writel(value | FSMC_DEVWID_16, &regs->bank_regs[bank].pc);
+               writel(value | FSMC_DEVWID_16, FSMC_NAND_REG(regs, bank, PC));
        else
-               writel(value | FSMC_DEVWID_8, &regs->bank_regs[bank].pc);
-
-       writel(readl(&regs->bank_regs[bank].pc) | FSMC_TCLR_1 | FSMC_TAR_1,
-              &regs->bank_regs[bank].pc);
-       writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
-              &regs->bank_regs[bank].comm);
-       writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
-              &regs->bank_regs[bank].attrib);
+               writel(value | FSMC_DEVWID_8, FSMC_NAND_REG(regs, bank, PC));
+
+       writel(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar,
+                       FSMC_NAND_REG(regs, bank, PC));
+       writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, COMM));
+       writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, ATTRIB));
 }
 
 /*
@@ -312,15 +443,15 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
 {
        struct fsmc_nand_data *host = container_of(mtd,
                                        struct fsmc_nand_data, mtd);
-       struct fsmc_regs *regs = host->regs_va;
+       void __iomem *regs = host->regs_va;
        uint32_t bank = host->bank;
 
-       writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ECCPLEN_256,
-                      &regs->bank_regs[bank].pc);
-       writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ECCEN,
-                       &regs->bank_regs[bank].pc);
-       writel(readl(&regs->bank_regs[bank].pc) | FSMC_ECCEN,
-                       &regs->bank_regs[bank].pc);
+       writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256,
+                       FSMC_NAND_REG(regs, bank, PC));
+       writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN,
+                       FSMC_NAND_REG(regs, bank, PC));
+       writel(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN,
+                       FSMC_NAND_REG(regs, bank, PC));
 }
 
 /*
@@ -333,37 +464,42 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
 {
        struct fsmc_nand_data *host = container_of(mtd,
                                        struct fsmc_nand_data, mtd);
-       struct fsmc_regs *regs = host->regs_va;
+       void __iomem *regs = host->regs_va;
        uint32_t bank = host->bank;
        uint32_t ecc_tmp;
        unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
 
        do {
-               if (readl(&regs->bank_regs[bank].sts) & FSMC_CODE_RDY)
+               if (readl(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY)
                        break;
                else
                        cond_resched();
        } while (!time_after_eq(jiffies, deadline));
 
-       ecc_tmp = readl(&regs->bank_regs[bank].ecc1);
+       if (time_after_eq(jiffies, deadline)) {
+               dev_err(host->dev, "calculate ecc timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1));
        ecc[0] = (uint8_t) (ecc_tmp >> 0);
        ecc[1] = (uint8_t) (ecc_tmp >> 8);
        ecc[2] = (uint8_t) (ecc_tmp >> 16);
        ecc[3] = (uint8_t) (ecc_tmp >> 24);
 
-       ecc_tmp = readl(&regs->bank_regs[bank].ecc2);
+       ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC2));
        ecc[4] = (uint8_t) (ecc_tmp >> 0);
        ecc[5] = (uint8_t) (ecc_tmp >> 8);
        ecc[6] = (uint8_t) (ecc_tmp >> 16);
        ecc[7] = (uint8_t) (ecc_tmp >> 24);
 
-       ecc_tmp = readl(&regs->bank_regs[bank].ecc3);
+       ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC3));
        ecc[8] = (uint8_t) (ecc_tmp >> 0);
        ecc[9] = (uint8_t) (ecc_tmp >> 8);
        ecc[10] = (uint8_t) (ecc_tmp >> 16);
        ecc[11] = (uint8_t) (ecc_tmp >> 24);
 
-       ecc_tmp = readl(&regs->bank_regs[bank].sts);
+       ecc_tmp = readl(FSMC_NAND_REG(regs, bank, STS));
        ecc[12] = (uint8_t) (ecc_tmp >> 16);
 
        return 0;
@@ -379,11 +515,11 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
 {
        struct fsmc_nand_data *host = container_of(mtd,
                                        struct fsmc_nand_data, mtd);
-       struct fsmc_regs *regs = host->regs_va;
+       void __iomem *regs = host->regs_va;
        uint32_t bank = host->bank;
        uint32_t ecc_tmp;
 
-       ecc_tmp = readl(&regs->bank_regs[bank].ecc1);
+       ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1));
        ecc[0] = (uint8_t) (ecc_tmp >> 0);
        ecc[1] = (uint8_t) (ecc_tmp >> 8);
        ecc[2] = (uint8_t) (ecc_tmp >> 16);
@@ -391,6 +527,166 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
        return 0;
 }
 
+/* Count the number of 0's in buff upto a max of max_bits */
+static int count_written_bits(uint8_t *buff, int size, int max_bits)
+{
+       int k, written_bits = 0;
+
+       for (k = 0; k < size; k++) {
+               written_bits += hweight8(~buff[k]);
+               if (written_bits > max_bits)
+                       break;
+       }
+
+       return written_bits;
+}
+
+static void dma_complete(void *param)
+{
+       struct fsmc_nand_data *host = param;
+
+       complete(&host->dma_access_complete);
+}
+
+static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
+               enum dma_data_direction direction)
+{
+       struct dma_chan *chan;
+       struct dma_device *dma_dev;
+       struct dma_async_tx_descriptor *tx;
+       dma_addr_t dma_dst, dma_src, dma_addr;
+       dma_cookie_t cookie;
+       unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+       int ret;
+
+       if (direction == DMA_TO_DEVICE)
+               chan = host->write_dma_chan;
+       else if (direction == DMA_FROM_DEVICE)
+               chan = host->read_dma_chan;
+       else
+               return -EINVAL;
+
+       dma_dev = chan->device;
+       dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction);
+
+       if (direction == DMA_TO_DEVICE) {
+               dma_src = dma_addr;
+               dma_dst = host->data_pa;
+               flags |= DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_SKIP_DEST_UNMAP;
+       } else {
+               dma_src = host->data_pa;
+               dma_dst = dma_addr;
+               flags |= DMA_COMPL_DEST_UNMAP_SINGLE | DMA_COMPL_SKIP_SRC_UNMAP;
+       }
+
+       tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src,
+                       len, flags);
+
+       if (!tx) {
+               dev_err(host->dev, "device_prep_dma_memcpy error\n");
+               dma_unmap_single(dma_dev->dev, dma_addr, len, direction);
+               return -EIO;
+       }
+
+       tx->callback = dma_complete;
+       tx->callback_param = host;
+       cookie = tx->tx_submit(tx);
+
+       ret = dma_submit_error(cookie);
+       if (ret) {
+               dev_err(host->dev, "dma_submit_error %d\n", cookie);
+               return ret;
+       }
+
+       dma_async_issue_pending(chan);
+
+       ret =
+       wait_for_completion_interruptible_timeout(&host->dma_access_complete,
+                               msecs_to_jiffies(3000));
+       if (ret <= 0) {
+               chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+               dev_err(host->dev, "wait_for_completion_timeout\n");
+               return ret ? ret : -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+/*
+ * fsmc_write_buf - write buffer to chip
+ * @mtd:       MTD device structure
+ * @buf:       data buffer
+ * @len:       number of bytes to write
+ */
+static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+       int i;
+       struct nand_chip *chip = mtd->priv;
+
+       if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
+                       IS_ALIGNED(len, sizeof(uint32_t))) {
+               uint32_t *p = (uint32_t *)buf;
+               len = len >> 2;
+               for (i = 0; i < len; i++)
+                       writel(p[i], chip->IO_ADDR_W);
+       } else {
+               for (i = 0; i < len; i++)
+                       writeb(buf[i], chip->IO_ADDR_W);
+       }
+}
+
+/*
+ * fsmc_read_buf - read chip data into buffer
+ * @mtd:       MTD device structure
+ * @buf:       buffer to store date
+ * @len:       number of bytes to read
+ */
+static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       int i;
+       struct nand_chip *chip = mtd->priv;
+
+       if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
+                       IS_ALIGNED(len, sizeof(uint32_t))) {
+               uint32_t *p = (uint32_t *)buf;
+               len = len >> 2;
+               for (i = 0; i < len; i++)
+                       p[i] = readl(chip->IO_ADDR_R);
+       } else {
+               for (i = 0; i < len; i++)
+                       buf[i] = readb(chip->IO_ADDR_R);
+       }
+}
+
+/*
+ * fsmc_read_buf_dma - read chip data into buffer
+ * @mtd:       MTD device structure
+ * @buf:       buffer to store date
+ * @len:       number of bytes to read
+ */
+static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       struct fsmc_nand_data *host;
+
+       host = container_of(mtd, struct fsmc_nand_data, mtd);
+       dma_xfer(host, buf, len, DMA_FROM_DEVICE);
+}
+
+/*
+ * fsmc_write_buf_dma - write buffer to chip
+ * @mtd:       MTD device structure
+ * @buf:       data buffer
+ * @len:       number of bytes to write
+ */
+static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
+               int len)
+{
+       struct fsmc_nand_data *host;
+
+       host = container_of(mtd, struct fsmc_nand_data, mtd);
+       dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE);
+}
+
 /*
  * fsmc_read_page_hwecc
  * @mtd:       mtd info structure
@@ -426,7 +722,6 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
        uint8_t *oob = (uint8_t *)&ecc_oob[0];
 
        for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
-
                chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page);
                chip->ecc.hwctl(mtd, NAND_ECC_READ);
                chip->read_buf(mtd, p, eccsize);
@@ -437,17 +732,19 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                        group++;
 
                        /*
-                       * length is intentionally kept a higher multiple of 2
-                       * to read at least 13 bytes even in case of 16 bit NAND
-                       * devices
-                       */
-                       len = roundup(len, 2);
+                        * length is intentionally kept a higher multiple of 2
+                        * to read at least 13 bytes even in case of 16 bit NAND
+                        * devices
+                        */
+                       if (chip->options & NAND_BUSWIDTH_16)
+                               len = roundup(len, 2);
+
                        chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page);
                        chip->read_buf(mtd, oob + j, len);
                        j += len;
                }
 
-               memcpy(&ecc_code[i], oob, 13);
+               memcpy(&ecc_code[i], oob, chip->ecc.bytes);
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 
                stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
@@ -461,7 +758,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
 }
 
 /*
- * fsmc_correct_data
+ * fsmc_bch8_correct_data
  * @mtd:       mtd info structure
  * @dat:       buffer of read data
  * @read_ecc:  ecc read from device spare area
@@ -470,19 +767,51 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
  * calc_ecc is a 104 bit information containing maximum of 8 error
  * offset informations of 13 bits each in 512 bytes of read data.
  */
-static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
+static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat,
                             uint8_t *read_ecc, uint8_t *calc_ecc)
 {
        struct fsmc_nand_data *host = container_of(mtd,
                                        struct fsmc_nand_data, mtd);
-       struct fsmc_regs *regs = host->regs_va;
+       struct nand_chip *chip = mtd->priv;
+       void __iomem *regs = host->regs_va;
        unsigned int bank = host->bank;
-       uint16_t err_idx[8];
-       uint64_t ecc_data[2];
+       uint32_t err_idx[8];
        uint32_t num_err, i;
+       uint32_t ecc1, ecc2, ecc3, ecc4;
+
+       num_err = (readl(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF;
+
+       /* no bit flipping */
+       if (likely(num_err == 0))
+               return 0;
+
+       /* too many errors */
+       if (unlikely(num_err > 8)) {
+               /*
+                * This is a temporary erase check. A newly erased page read
+                * would result in an ecc error because the oob data is also
+                * erased to FF and the calculated ecc for an FF data is not
+                * FF..FF.
+                * This is a workaround to skip performing correction in case
+                * data is FF..FF
+                *
+                * Logic:
+                * For every page, each bit written as 0 is counted until these
+                * number of bits are greater than 8 (the maximum correction
+                * capability of FSMC for each 512 + 13 bytes)
+                */
+
+               int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8);
+               int bits_data = count_written_bits(dat, chip->ecc.size, 8);
+
+               if ((bits_ecc + bits_data) <= 8) {
+                       if (bits_data)
+                               memset(dat, 0xff, chip->ecc.size);
+                       return bits_data;
+               }
 
-       /* The calculated ecc is actually the correction index in data */
-       memcpy(ecc_data, calc_ecc, 13);
+               return -EBADMSG;
+       }
 
        /*
         * ------------------- calc_ecc[] bit wise -----------|--13 bits--|
@@ -493,27 +822,26 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
         * uint64_t array and error offset indexes are populated in err_idx
         * array
         */
-       for (i = 0; i < 8; i++) {
-               if (i == 4) {
-                       err_idx[4] = ((ecc_data[1] & 0x1) << 12) | ecc_data[0];
-                       ecc_data[1] >>= 1;
-                       continue;
-               }
-               err_idx[i] = (ecc_data[i/4] & 0x1FFF);
-               ecc_data[i/4] >>= 13;
-       }
-
-       num_err = (readl(&regs->bank_regs[bank].sts) >> 10) & 0xF;
-
-       if (num_err == 0xF)
-               return -EBADMSG;
+       ecc1 = readl(FSMC_NAND_REG(regs, bank, ECC1));
+       ecc2 = readl(FSMC_NAND_REG(regs, bank, ECC2));
+       ecc3 = readl(FSMC_NAND_REG(regs, bank, ECC3));
+       ecc4 = readl(FSMC_NAND_REG(regs, bank, STS));
+
+       err_idx[0] = (ecc1 >> 0) & 0x1FFF;
+       err_idx[1] = (ecc1 >> 13) & 0x1FFF;
+       err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F);
+       err_idx[3] = (ecc2 >> 7) & 0x1FFF;
+       err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF);
+       err_idx[5] = (ecc3 >> 1) & 0x1FFF;
+       err_idx[6] = (ecc3 >> 14) & 0x1FFF;
+       err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F);
 
        i = 0;
        while (num_err--) {
                change_bit(0, (unsigned long *)&err_idx[i]);
                change_bit(1, (unsigned long *)&err_idx[i]);
 
-               if (err_idx[i] <= 512 * 8) {
+               if (err_idx[i] < chip->ecc.size * 8) {
                        change_bit(err_idx[i], (unsigned long *)dat);
                        i++;
                }
@@ -521,6 +849,44 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
        return i;
 }
 
+static bool filter(struct dma_chan *chan, void *slave)
+{
+       chan->private = slave;
+       return true;
+}
+
+#ifdef CONFIG_OF
+static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev,
+                                              struct device_node *np)
+{
+       struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       u32 val;
+
+       /* Set default NAND width to 8 bits */
+       pdata->width = 8;
+       if (!of_property_read_u32(np, "bank-width", &val)) {
+               if (val == 2) {
+                       pdata->width = 16;
+               } else if (val != 1) {
+                       dev_err(&pdev->dev, "invalid bank-width %u\n", val);
+                       return -EINVAL;
+               }
+       }
+       of_property_read_u32(np, "st,ale-off", &pdata->ale_off);
+       of_property_read_u32(np, "st,cle-off", &pdata->cle_off);
+       if (of_get_property(np, "nand-skip-bbtscan", NULL))
+               pdata->options = NAND_SKIP_BBTSCAN;
+
+       return 0;
+}
+#else
+static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev,
+                                              struct device_node *np)
+{
+       return -ENOSYS;
+}
+#endif
+
 /*
  * fsmc_nand_probe - Probe function
  * @pdev:       platform device structure
@@ -528,102 +894,109 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
 static int __init fsmc_nand_probe(struct platform_device *pdev)
 {
        struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct device_node __maybe_unused *np = pdev->dev.of_node;
+       struct mtd_part_parser_data ppdata = {};
        struct fsmc_nand_data *host;
        struct mtd_info *mtd;
        struct nand_chip *nand;
-       struct fsmc_regs *regs;
        struct resource *res;
+       dma_cap_mask_t mask;
        int ret = 0;
        u32 pid;
        int i;
 
+       if (np) {
+               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+               pdev->dev.platform_data = pdata;
+               ret = fsmc_nand_probe_config_dt(pdev, np);
+               if (ret) {
+                       dev_err(&pdev->dev, "no platform data\n");
+                       return -ENODEV;
+               }
+       }
+
        if (!pdata) {
                dev_err(&pdev->dev, "platform data is NULL\n");
                return -EINVAL;
        }
 
        /* Allocate memory for the device structure (and zero it) */
-       host = kzalloc(sizeof(*host), GFP_KERNEL);
+       host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
        if (!host) {
                dev_err(&pdev->dev, "failed to allocate device structure\n");
                return -ENOMEM;
        }
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
-       if (!res) {
-               ret = -EIO;
-               goto err_probe1;
-       }
+       if (!res)
+               return -EINVAL;
 
-       host->resdata = request_mem_region(res->start, resource_size(res),
-                       pdev->name);
-       if (!host->resdata) {
-               ret = -EIO;
-               goto err_probe1;
+       if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+                               pdev->name)) {
+               dev_err(&pdev->dev, "Failed to get memory data resourse\n");
+               return -ENOENT;
        }
 
-       host->data_va = ioremap(res->start, resource_size(res));
+       host->data_pa = (dma_addr_t)res->start;
+       host->data_va = devm_ioremap(&pdev->dev, res->start,
+                       resource_size(res));
        if (!host->data_va) {
-               ret = -EIO;
-               goto err_probe1;
+               dev_err(&pdev->dev, "data ioremap failed\n");
+               return -ENOMEM;
        }
 
-       host->resaddr = request_mem_region(res->start + PLAT_NAND_ALE,
-                       resource_size(res), pdev->name);
-       if (!host->resaddr) {
-               ret = -EIO;
-               goto err_probe1;
+       if (!devm_request_mem_region(&pdev->dev, res->start + pdata->ale_off,
+                       resource_size(res), pdev->name)) {
+               dev_err(&pdev->dev, "Failed to get memory ale resourse\n");
+               return -ENOENT;
        }
 
-       host->addr_va = ioremap(res->start + PLAT_NAND_ALE, resource_size(res));
+       host->addr_va = devm_ioremap(&pdev->dev, res->start + pdata->ale_off,
+                       resource_size(res));
        if (!host->addr_va) {
-               ret = -EIO;
-               goto err_probe1;
+               dev_err(&pdev->dev, "ale ioremap failed\n");
+               return -ENOMEM;
        }
 
-       host->rescmd = request_mem_region(res->start + PLAT_NAND_CLE,
-                       resource_size(res), pdev->name);
-       if (!host->rescmd) {
-               ret = -EIO;
-               goto err_probe1;
+       if (!devm_request_mem_region(&pdev->dev, res->start + pdata->cle_off,
+                       resource_size(res), pdev->name)) {
+               dev_err(&pdev->dev, "Failed to get memory cle resourse\n");
+               return -ENOENT;
        }
 
-       host->cmd_va = ioremap(res->start + PLAT_NAND_CLE, resource_size(res));
+       host->cmd_va = devm_ioremap(&pdev->dev, res->start + pdata->cle_off,
+                       resource_size(res));
        if (!host->cmd_va) {
-               ret = -EIO;
-               goto err_probe1;
+               dev_err(&pdev->dev, "ale ioremap failed\n");
+               return -ENOMEM;
        }
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
-       if (!res) {
-               ret = -EIO;
-               goto err_probe1;
-       }
+       if (!res)
+               return -EINVAL;
 
-       host->resregs = request_mem_region(res->start, resource_size(res),
-                       pdev->name);
-       if (!host->resregs) {
-               ret = -EIO;
-               goto err_probe1;
+       if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+                       pdev->name)) {
+               dev_err(&pdev->dev, "Failed to get memory regs resourse\n");
+               return -ENOENT;
        }
 
-       host->regs_va = ioremap(res->start, resource_size(res));
+       host->regs_va = devm_ioremap(&pdev->dev, res->start,
+                       resource_size(res));
        if (!host->regs_va) {
-               ret = -EIO;
-               goto err_probe1;
+               dev_err(&pdev->dev, "regs ioremap failed\n");
+               return -ENOMEM;
        }
 
        host->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(host->clk)) {
                dev_err(&pdev->dev, "failed to fetch block clock\n");
-               ret = PTR_ERR(host->clk);
-               host->clk = NULL;
-               goto err_probe1;
+               return PTR_ERR(host->clk);
        }
 
        ret = clk_enable(host->clk);
        if (ret)
-               goto err_probe1;
+               goto err_clk_enable;
 
        /*
         * This device ID is actually a common AMBA ID as used on the
@@ -639,7 +1012,14 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
 
        host->bank = pdata->bank;
        host->select_chip = pdata->select_bank;
-       regs = host->regs_va;
+       host->partitions = pdata->partitions;
+       host->nr_partitions = pdata->nr_partitions;
+       host->dev = &pdev->dev;
+       host->dev_timings = pdata->nand_timings;
+       host->mode = pdata->mode;
+
+       if (host->mode == USE_DMA_ACCESS)
+               init_completion(&host->dma_access_complete);
 
        /* Link all private pointers */
        mtd = &host->mtd;
@@ -658,21 +1038,53 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
        nand->ecc.size = 512;
        nand->options = pdata->options;
        nand->select_chip = fsmc_select_chip;
+       nand->badblockbits = 7;
 
        if (pdata->width == FSMC_NAND_BW16)
                nand->options |= NAND_BUSWIDTH_16;
 
-       fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16);
+       switch (host->mode) {
+       case USE_DMA_ACCESS:
+               dma_cap_zero(mask);
+               dma_cap_set(DMA_MEMCPY, mask);
+               host->read_dma_chan = dma_request_channel(mask, filter,
+                               pdata->read_dma_priv);
+               if (!host->read_dma_chan) {
+                       dev_err(&pdev->dev, "Unable to get read dma channel\n");
+                       goto err_req_read_chnl;
+               }
+               host->write_dma_chan = dma_request_channel(mask, filter,
+                               pdata->write_dma_priv);
+               if (!host->write_dma_chan) {
+                       dev_err(&pdev->dev, "Unable to get write dma channel\n");
+                       goto err_req_write_chnl;
+               }
+               nand->read_buf = fsmc_read_buf_dma;
+               nand->write_buf = fsmc_write_buf_dma;
+               break;
+
+       default:
+       case USE_WORD_ACCESS:
+               nand->read_buf = fsmc_read_buf;
+               nand->write_buf = fsmc_write_buf;
+               break;
+       }
+
+       fsmc_nand_setup(host->regs_va, host->bank,
+                       nand->options & NAND_BUSWIDTH_16,
+                       host->dev_timings);
 
        if (AMBA_REV_BITS(host->pid) >= 8) {
                nand->ecc.read_page = fsmc_read_page_hwecc;
                nand->ecc.calculate = fsmc_read_hwecc_ecc4;
-               nand->ecc.correct = fsmc_correct_data;
+               nand->ecc.correct = fsmc_bch8_correct_data;
                nand->ecc.bytes = 13;
+               nand->ecc.strength = 8;
        } else {
                nand->ecc.calculate = fsmc_read_hwecc_ecc1;
                nand->ecc.correct = nand_correct_data;
                nand->ecc.bytes = 3;
+               nand->ecc.strength = 1;
        }
 
        /*
@@ -681,19 +1093,52 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
        if (nand_scan_ident(&host->mtd, 1, NULL)) {
                ret = -ENXIO;
                dev_err(&pdev->dev, "No NAND Device found!\n");
-               goto err_probe;
+               goto err_scan_ident;
        }
 
        if (AMBA_REV_BITS(host->pid) >= 8) {
-               if (host->mtd.writesize == 512) {
-                       nand->ecc.layout = &fsmc_ecc4_sp_layout;
+               switch (host->mtd.oobsize) {
+               case 16:
+                       nand->ecc.layout = &fsmc_ecc4_16_layout;
                        host->ecc_place = &fsmc_ecc4_sp_place;
-               } else {
-                       nand->ecc.layout = &fsmc_ecc4_lp_layout;
+                       break;
+               case 64:
+                       nand->ecc.layout = &fsmc_ecc4_64_layout;
+                       host->ecc_place = &fsmc_ecc4_lp_place;
+                       break;
+               case 128:
+                       nand->ecc.layout = &fsmc_ecc4_128_layout;
+                       host->ecc_place = &fsmc_ecc4_lp_place;
+                       break;
+               case 224:
+                       nand->ecc.layout = &fsmc_ecc4_224_layout;
                        host->ecc_place = &fsmc_ecc4_lp_place;
+                       break;
+               case 256:
+                       nand->ecc.layout = &fsmc_ecc4_256_layout;
+                       host->ecc_place = &fsmc_ecc4_lp_place;
+                       break;
+               default:
+                       printk(KERN_WARNING "No oob scheme defined for "
+                              "oobsize %d\n", mtd->oobsize);
+                       BUG();
                }
        } else {
-               nand->ecc.layout = &fsmc_ecc1_layout;
+               switch (host->mtd.oobsize) {
+               case 16:
+                       nand->ecc.layout = &fsmc_ecc1_16_layout;
+                       break;
+               case 64:
+                       nand->ecc.layout = &fsmc_ecc1_64_layout;
+                       break;
+               case 128:
+                       nand->ecc.layout = &fsmc_ecc1_128_layout;
+                       break;
+               default:
+                       printk(KERN_WARNING "No oob scheme defined for "
+                              "oobsize %d\n", mtd->oobsize);
+                       BUG();
+               }
        }
 
        /* Second stage of scan to fill MTD data-structures */
@@ -713,13 +1158,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
         * Check for partition info passed
         */
        host->mtd.name = "nand";
-       ret = mtd_device_parse_register(&host->mtd, NULL, 0,
-                       host->mtd.size <= 0x04000000 ?
-                               partition_info_16KB_blk :
-                               partition_info_128KB_blk,
-                       host->mtd.size <= 0x04000000 ?
-                               ARRAY_SIZE(partition_info_16KB_blk) :
-                               ARRAY_SIZE(partition_info_128KB_blk));
+       ppdata.of_node = np;
+       ret = mtd_device_parse_register(&host->mtd, NULL, &ppdata,
+                                       host->partitions, host->nr_partitions);
        if (ret)
                goto err_probe;
 
@@ -728,32 +1169,16 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
        return 0;
 
 err_probe:
+err_scan_ident:
+       if (host->mode == USE_DMA_ACCESS)
+               dma_release_channel(host->write_dma_chan);
+err_req_write_chnl:
+       if (host->mode == USE_DMA_ACCESS)
+               dma_release_channel(host->read_dma_chan);
+err_req_read_chnl:
        clk_disable(host->clk);
-err_probe1:
-       if (host->clk)
-               clk_put(host->clk);
-       if (host->regs_va)
-               iounmap(host->regs_va);
-       if (host->resregs)
-               release_mem_region(host->resregs->start,
-                               resource_size(host->resregs));
-       if (host->cmd_va)
-               iounmap(host->cmd_va);
-       if (host->rescmd)
-               release_mem_region(host->rescmd->start,
-                               resource_size(host->rescmd));
-       if (host->addr_va)
-               iounmap(host->addr_va);
-       if (host->resaddr)
-               release_mem_region(host->resaddr->start,
-                               resource_size(host->resaddr));
-       if (host->data_va)
-               iounmap(host->data_va);
-       if (host->resdata)
-               release_mem_region(host->resdata->start,
-                               resource_size(host->resdata));
-
-       kfree(host);
+err_clk_enable:
+       clk_put(host->clk);
        return ret;
 }
 
@@ -768,24 +1193,15 @@ static int fsmc_nand_remove(struct platform_device *pdev)
 
        if (host) {
                nand_release(&host->mtd);
+
+               if (host->mode == USE_DMA_ACCESS) {
+                       dma_release_channel(host->write_dma_chan);
+                       dma_release_channel(host->read_dma_chan);
+               }
                clk_disable(host->clk);
                clk_put(host->clk);
-
-               iounmap(host->regs_va);
-               release_mem_region(host->resregs->start,
-                               resource_size(host->resregs));
-               iounmap(host->cmd_va);
-               release_mem_region(host->rescmd->start,
-                               resource_size(host->rescmd));
-               iounmap(host->addr_va);
-               release_mem_region(host->resaddr->start,
-                               resource_size(host->resaddr));
-               iounmap(host->data_va);
-               release_mem_region(host->resdata->start,
-                               resource_size(host->resdata));
-
-               kfree(host);
        }
+
        return 0;
 }
 
@@ -801,15 +1217,24 @@ static int fsmc_nand_suspend(struct device *dev)
 static int fsmc_nand_resume(struct device *dev)
 {
        struct fsmc_nand_data *host = dev_get_drvdata(dev);
-       if (host)
+       if (host) {
                clk_enable(host->clk);
+               fsmc_nand_setup(host->regs_va, host->bank,
+                               host->nand.options & NAND_BUSWIDTH_16,
+                               host->dev_timings);
+       }
        return 0;
 }
 
-static const struct dev_pm_ops fsmc_nand_pm_ops = {
-       .suspend = fsmc_nand_suspend,
-       .resume = fsmc_nand_resume,
+static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id fsmc_nand_id_table[] = {
+       { .compatible = "st,spear600-fsmc-nand" },
+       {}
 };
+MODULE_DEVICE_TABLE(of, fsmc_nand_id_table);
 #endif
 
 static struct platform_driver fsmc_nand_driver = {
@@ -817,6 +1242,7 @@ static struct platform_driver fsmc_nand_driver = {
        .driver = {
                .owner = THIS_MODULE,
                .name = "fsmc-nand",
+               .of_match_table = of_match_ptr(fsmc_nand_id_table),
 #ifdef CONFIG_PM
                .pm = &fsmc_nand_pm_ops,
 #endif
index 590dd5cceed66164ff93686f9f355818883ac0b3..e8ea7107932e9a9f784007da5be11cf01e32d5dd 100644 (file)
@@ -848,7 +848,10 @@ int gpmi_send_command(struct gpmi_nand_data *this)
 
        sg_init_one(sgl, this->cmd_buffer, this->command_length);
        dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE);
-       desc = dmaengine_prep_slave_sg(channel, sgl, 1, DMA_MEM_TO_DEV, 1);
+       desc = dmaengine_prep_slave_sg(channel,
+                               sgl, 1, DMA_MEM_TO_DEV,
+                               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
        if (!desc) {
                pr_err("step 2 error\n");
                return -1;
@@ -889,7 +892,8 @@ int gpmi_send_data(struct gpmi_nand_data *this)
        /* [2] send DMA request */
        prepare_data_dma(this, DMA_TO_DEVICE);
        desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
-                                               1, DMA_MEM_TO_DEV, 1);
+                                       1, DMA_MEM_TO_DEV,
+                                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!desc) {
                pr_err("step 2 error\n");
                return -1;
@@ -925,7 +929,8 @@ int gpmi_read_data(struct gpmi_nand_data *this)
        /* [2] : send DMA request */
        prepare_data_dma(this, DMA_FROM_DEVICE);
        desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
-                                       1, DMA_DEV_TO_MEM, 1);
+                                       1, DMA_DEV_TO_MEM,
+                                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!desc) {
                pr_err("step 2 error\n");
                return -1;
@@ -970,8 +975,10 @@ int gpmi_send_page(struct gpmi_nand_data *this,
        pio[4] = payload;
        pio[5] = auxiliary;
 
-       desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio,
-                                       ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
+       desc = dmaengine_prep_slave_sg(channel,
+                                       (struct scatterlist *)pio,
+                                       ARRAY_SIZE(pio), DMA_TRANS_NONE,
+                                       DMA_CTRL_ACK);
        if (!desc) {
                pr_err("step 2 error\n");
                return -1;
@@ -1035,7 +1042,8 @@ int gpmi_read_page(struct gpmi_nand_data *this,
        pio[5] = auxiliary;
        desc = dmaengine_prep_slave_sg(channel,
                                        (struct scatterlist *)pio,
-                                       ARRAY_SIZE(pio), DMA_TRANS_NONE, 1);
+                                       ARRAY_SIZE(pio), DMA_TRANS_NONE,
+                                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!desc) {
                pr_err("step 2 error\n");
                return -1;
@@ -1052,9 +1060,11 @@ int gpmi_read_page(struct gpmi_nand_data *this,
                | BF_GPMI_CTRL0_ADDRESS(address)
                | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size);
        pio[1] = 0;
+       pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */
        desc = dmaengine_prep_slave_sg(channel,
-                               (struct scatterlist *)pio, 2,
-                               DMA_TRANS_NONE, 1);
+                               (struct scatterlist *)pio, 3,
+                               DMA_TRANS_NONE,
+                               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!desc) {
                pr_err("step 3 error\n");
                return -1;
index 493ec2fcf97fe7f8c21e55420e4ecb3ddaa6c845..75b1dde1635884986097786bab730e2264c01072 100644 (file)
@@ -1124,7 +1124,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
                chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
 
        /* Do we have a flash based bad block table ? */
-       if (chip->options & NAND_BBT_USE_FLASH)
+       if (chip->bbt_options & NAND_BBT_USE_FLASH)
                ret = nand_update_bbt(mtd, ofs);
        else {
                chipnr = (int)(ofs >> chip->chip_shift);
@@ -1155,7 +1155,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
        return ret;
 }
 
-static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this)
+static int nand_boot_set_geometry(struct gpmi_nand_data *this)
 {
        struct boot_rom_geometry *geometry = &this->rom_geometry;
 
@@ -1182,7 +1182,7 @@ static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this)
 }
 
 static const char  *fingerprint = "STMP";
-static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this)
+static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
 {
        struct boot_rom_geometry *rom_geo = &this->rom_geometry;
        struct device *dev = this->dev;
@@ -1239,7 +1239,7 @@ static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this)
 }
 
 /* Writes a transcription stamp. */
-static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this)
+static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
 {
        struct device *dev = this->dev;
        struct boot_rom_geometry *rom_geo = &this->rom_geometry;
@@ -1322,7 +1322,7 @@ static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this)
        return 0;
 }
 
-static int __devinit mx23_boot_init(struct gpmi_nand_data  *this)
+static int mx23_boot_init(struct gpmi_nand_data  *this)
 {
        struct device *dev = this->dev;
        struct nand_chip *chip = &this->nand;
@@ -1391,7 +1391,7 @@ static int __devinit mx23_boot_init(struct gpmi_nand_data  *this)
        return 0;
 }
 
-static int __devinit nand_boot_init(struct gpmi_nand_data  *this)
+static int nand_boot_init(struct gpmi_nand_data  *this)
 {
        nand_boot_set_geometry(this);
 
@@ -1401,7 +1401,7 @@ static int __devinit nand_boot_init(struct gpmi_nand_data  *this)
        return 0;
 }
 
-static int __devinit gpmi_set_geometry(struct gpmi_nand_data *this)
+static int gpmi_set_geometry(struct gpmi_nand_data *this)
 {
        int ret;
 
index e023bccb7781b95febd36b053f18ee03567cc84f..ec6180d4ff8ffa22314ce923802c606910492166 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/mtd/nand.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
-#include <mach/dma.h>
+#include <linux/fsl/mxs-dma.h>
 
 struct resources {
        void          *gpmi_regs;
index 5dc6f0d92f1af7be563fd269b70ee505b0a08b9c..11e487813428735082ce544ace9d150345ef68d1 100644 (file)
@@ -135,8 +135,8 @@ static int __init h1910_init(void)
        }
 
        /* Register the partitions */
-       mtd_device_parse_register(h1910_nand_mtd, NULL, 0,
-                       partition_info, NUM_PARTITIONS);
+       mtd_device_parse_register(h1910_nand_mtd, NULL, NULL, partition_info,
+                                 NUM_PARTITIONS);
 
        /* Return happy */
        return 0;
index ac3b9f255e009bd2696688c5d3673e60395be5d7..e4147e8acb7c560f2a8c3fdae68bf1027266fdba 100644 (file)
@@ -332,6 +332,11 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
        chip->ecc.mode          = NAND_ECC_HW_OOB_FIRST;
        chip->ecc.size          = 512;
        chip->ecc.bytes         = 9;
+       chip->ecc.strength      = 2;
+       /*
+        * FIXME: ecc_strength value of 2 bits per 512 bytes of data is a
+        * conservative guess, given 9 ecc bytes and reed-solomon alg.
+        */
 
        if (pdata)
                chip->ecc.layout = pdata->ecc_layout;
@@ -367,9 +372,9 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
                goto err_gpio_free;
        }
 
-       ret = mtd_device_parse_register(mtd, NULL, 0,
-                       pdata ? pdata->partitions : NULL,
-                       pdata ? pdata->num_partitions : 0);
+       ret = mtd_device_parse_register(mtd, NULL, NULL,
+                                       pdata ? pdata->partitions : NULL,
+                                       pdata ? pdata->num_partitions : 0);
 
        if (ret) {
                dev_err(&pdev->dev, "Failed to add mtd device\n");
index 74a43b818d0e0401616dcc5ace7af52bcf53c726..cc0678a967c12c6b054d21ee3fe5b868ee997924 100644 (file)
@@ -1225,9 +1225,16 @@ static int __init mxcnd_probe(struct platform_device *pdev)
                goto escan;
        }
 
+       if (this->ecc.mode == NAND_ECC_HW) {
+               if (nfc_is_v1())
+                       this->ecc.strength = 1;
+               else
+                       this->ecc.strength = (host->eccsize == 4) ? 4 : 8;
+       }
+
        /* Register the partitions */
-       mtd_device_parse_register(mtd, part_probes, 0,
-                       pdata->parts, pdata->nr_parts);
+       mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts,
+                                 pdata->nr_parts);
 
        platform_set_drvdata(pdev, host);
 
index 8a393f9e6027d1be3dcf3d149dfc277e8941fca2..47b19c0bb070e3da612b031848496f7551c2589f 100644 (file)
@@ -123,12 +123,6 @@ static int check_offs_len(struct mtd_info *mtd,
                ret = -EINVAL;
        }
 
-       /* Do not allow past end of device */
-       if (ofs + len > mtd->size) {
-               pr_debug("%s: past end of device\n", __func__);
-               ret = -EINVAL;
-       }
-
        return ret;
 }
 
@@ -338,7 +332,7 @@ static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
  */
 static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 {
-       int page, chipnr, res = 0;
+       int page, chipnr, res = 0, i = 0;
        struct nand_chip *chip = mtd->priv;
        u16 bad;
 
@@ -356,23 +350,29 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
                chip->select_chip(mtd, chipnr);
        }
 
-       if (chip->options & NAND_BUSWIDTH_16) {
-               chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
-                             page);
-               bad = cpu_to_le16(chip->read_word(mtd));
-               if (chip->badblockpos & 0x1)
-                       bad >>= 8;
-               else
-                       bad &= 0xFF;
-       } else {
-               chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
-               bad = chip->read_byte(mtd);
-       }
+       do {
+               if (chip->options & NAND_BUSWIDTH_16) {
+                       chip->cmdfunc(mtd, NAND_CMD_READOOB,
+                                       chip->badblockpos & 0xFE, page);
+                       bad = cpu_to_le16(chip->read_word(mtd));
+                       if (chip->badblockpos & 0x1)
+                               bad >>= 8;
+                       else
+                               bad &= 0xFF;
+               } else {
+                       chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
+                                       page);
+                       bad = chip->read_byte(mtd);
+               }
 
-       if (likely(chip->badblockbits == 8))
-               res = bad != 0xFF;
-       else
-               res = hweight8(bad) < chip->badblockbits;
+               if (likely(chip->badblockbits == 8))
+                       res = bad != 0xFF;
+               else
+                       res = hweight8(bad) < chip->badblockbits;
+               ofs += mtd->writesize;
+               page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+               i++;
+       } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
 
        if (getchip)
                nand_release_device(mtd);
@@ -386,51 +386,79 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
  * @ofs: offset from device start
  *
  * This is the default implementation, which can be overridden by a hardware
- * specific driver.
+ * specific driver. We try operations in the following order, according to our
+ * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH):
+ *  (1) erase the affected block, to allow OOB marker to be written cleanly
+ *  (2) update in-memory BBT
+ *  (3) write bad block marker to OOB area of affected block
+ *  (4) update flash-based BBT
+ * Note that we retain the first error encountered in (3) or (4), finish the
+ * procedures, and dump the error in the end.
 */
 static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
        struct nand_chip *chip = mtd->priv;
        uint8_t buf[2] = { 0, 0 };
-       int block, ret, i = 0;
+       int block, res, ret = 0, i = 0;
+       int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM);
 
-       if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
-               ofs += mtd->erasesize - mtd->writesize;
+       if (write_oob) {
+               struct erase_info einfo;
+
+               /* Attempt erase before marking OOB */
+               memset(&einfo, 0, sizeof(einfo));
+               einfo.mtd = mtd;
+               einfo.addr = ofs;
+               einfo.len = 1 << chip->phys_erase_shift;
+               nand_erase_nand(mtd, &einfo, 0);
+       }
 
        /* Get block number */
        block = (int)(ofs >> chip->bbt_erase_shift);
+       /* Mark block bad in memory-based BBT */
        if (chip->bbt)
                chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
 
-       /* Do we have a flash based bad block table? */
-       if (chip->bbt_options & NAND_BBT_USE_FLASH)
-               ret = nand_update_bbt(mtd, ofs);
-       else {
+       /* Write bad block marker to OOB */
+       if (write_oob) {
                struct mtd_oob_ops ops;
+               loff_t wr_ofs = ofs;
 
                nand_get_device(chip, mtd, FL_WRITING);
 
-               /*
-                * Write to first two pages if necessary. If we write to more
-                * than one location, the first error encountered quits the
-                * procedure. We write two bytes per location, so we dont have
-                * to mess with 16 bit access.
-                */
-               ops.len = ops.ooblen = 2;
                ops.datbuf = NULL;
                ops.oobbuf = buf;
-               ops.ooboffs = chip->badblockpos & ~0x01;
+               ops.ooboffs = chip->badblockpos;
+               if (chip->options & NAND_BUSWIDTH_16) {
+                       ops.ooboffs &= ~0x01;
+                       ops.len = ops.ooblen = 2;
+               } else {
+                       ops.len = ops.ooblen = 1;
+               }
                ops.mode = MTD_OPS_PLACE_OOB;
+
+               /* Write to first/last page(s) if necessary */
+               if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+                       wr_ofs += mtd->erasesize - mtd->writesize;
                do {
-                       ret = nand_do_write_oob(mtd, ofs, &ops);
+                       res = nand_do_write_oob(mtd, wr_ofs, &ops);
+                       if (!ret)
+                               ret = res;
 
                        i++;
-                       ofs += mtd->writesize;
-               } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) &&
-                               i < 2);
+                       wr_ofs += mtd->writesize;
+               } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
 
                nand_release_device(mtd);
        }
+
+       /* Update flash-based bad block table */
+       if (chip->bbt_options & NAND_BBT_USE_FLASH) {
+               res = nand_update_bbt(mtd, ofs);
+               if (!ret)
+                       ret = res;
+       }
+
        if (!ret)
                mtd->ecc_stats.badblocks++;
 
@@ -1586,25 +1614,14 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
        struct mtd_oob_ops ops;
        int ret;
 
-       /* Do not allow reads past end of device */
-       if ((from + len) > mtd->size)
-               return -EINVAL;
-       if (!len)
-               return 0;
-
        nand_get_device(chip, mtd, FL_READING);
-
        ops.len = len;
        ops.datbuf = buf;
        ops.oobbuf = NULL;
        ops.mode = 0;
-
        ret = nand_do_read_ops(mtd, from, &ops);
-
        *retlen = ops.retlen;
-
        nand_release_device(mtd);
-
        return ret;
 }
 
@@ -2293,12 +2310,6 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
        struct mtd_oob_ops ops;
        int ret;
 
-       /* Do not allow reads past end of device */
-       if ((to + len) > mtd->size)
-               return -EINVAL;
-       if (!len)
-               return 0;
-
        /* Wait for the device to get ready */
        panic_nand_wait(mtd, chip, 400);
 
@@ -2333,25 +2344,14 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
        struct mtd_oob_ops ops;
        int ret;
 
-       /* Do not allow reads past end of device */
-       if ((to + len) > mtd->size)
-               return -EINVAL;
-       if (!len)
-               return 0;
-
        nand_get_device(chip, mtd, FL_WRITING);
-
        ops.len = len;
        ops.datbuf = (uint8_t *)buf;
        ops.oobbuf = NULL;
        ops.mode = 0;
-
        ret = nand_do_write_ops(mtd, to, &ops);
-
        *retlen = ops.retlen;
-
        nand_release_device(mtd);
-
        return ret;
 }
 
@@ -2550,8 +2550,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
        if (check_offs_len(mtd, instr->addr, instr->len))
                return -EINVAL;
 
-       instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
-
        /* Grab the lock and see if the device is available */
        nand_get_device(chip, mtd, FL_ERASING);
 
@@ -2715,10 +2713,6 @@ static void nand_sync(struct mtd_info *mtd)
  */
 static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
 {
-       /* Check for invalid offset */
-       if (offs > mtd->size)
-               return -EINVAL;
-
        return nand_block_checkbad(mtd, offs, 1, 0);
 }
 
@@ -2857,7 +2851,6 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
                chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
                return 0;
 
-       pr_info("ONFI flash detected\n");
        chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
        for (i = 0; i < 3; i++) {
                chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
@@ -2898,7 +2891,8 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
        mtd->writesize = le32_to_cpu(p->byte_per_page);
        mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
        mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
-       chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+       chip->chipsize = le32_to_cpu(p->blocks_per_lun);
+       chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
        *busw = 0;
        if (le16_to_cpu(p->features) & 1)
                *busw = NAND_BUSWIDTH_16;
@@ -2907,6 +2901,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
        chip->options |= (NAND_NO_READRDY |
                        NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
 
+       pr_info("ONFI flash detected\n");
        return 1;
 }
 
@@ -3238,6 +3233,10 @@ int nand_scan_tail(struct mtd_info *mtd)
        int i;
        struct nand_chip *chip = mtd->priv;
 
+       /* New bad blocks should be marked in OOB, flash-based BBT, or both */
+       BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+                       !(chip->bbt_options & NAND_BBT_USE_FLASH));
+
        if (!(chip->options & NAND_OWN_BUFFERS))
                chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
        if (!chip->buffers)
@@ -3350,6 +3349,7 @@ int nand_scan_tail(struct mtd_info *mtd)
                if (!chip->ecc.size)
                        chip->ecc.size = 256;
                chip->ecc.bytes = 3;
+               chip->ecc.strength = 1;
                break;
 
        case NAND_ECC_SOFT_BCH:
@@ -3384,6 +3384,8 @@ int nand_scan_tail(struct mtd_info *mtd)
                        pr_warn("BCH ECC initialization failed!\n");
                        BUG();
                }
+               chip->ecc.strength =
+                       chip->ecc.bytes*8 / fls(8*chip->ecc.size);
                break;
 
        case NAND_ECC_NONE:
@@ -3397,6 +3399,7 @@ int nand_scan_tail(struct mtd_info *mtd)
                chip->ecc.write_oob = nand_write_oob_std;
                chip->ecc.size = mtd->writesize;
                chip->ecc.bytes = 0;
+               chip->ecc.strength = 0;
                break;
 
        default:
@@ -3461,25 +3464,26 @@ int nand_scan_tail(struct mtd_info *mtd)
        mtd->type = MTD_NANDFLASH;
        mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
                                                MTD_CAP_NANDFLASH;
-       mtd->erase = nand_erase;
-       mtd->point = NULL;
-       mtd->unpoint = NULL;
-       mtd->read = nand_read;
-       mtd->write = nand_write;
-       mtd->panic_write = panic_nand_write;
-       mtd->read_oob = nand_read_oob;
-       mtd->write_oob = nand_write_oob;
-       mtd->sync = nand_sync;
-       mtd->lock = NULL;
-       mtd->unlock = NULL;
-       mtd->suspend = nand_suspend;
-       mtd->resume = nand_resume;
-       mtd->block_isbad = nand_block_isbad;
-       mtd->block_markbad = nand_block_markbad;
+       mtd->_erase = nand_erase;
+       mtd->_point = NULL;
+       mtd->_unpoint = NULL;
+       mtd->_read = nand_read;
+       mtd->_write = nand_write;
+       mtd->_panic_write = panic_nand_write;
+       mtd->_read_oob = nand_read_oob;
+       mtd->_write_oob = nand_write_oob;
+       mtd->_sync = nand_sync;
+       mtd->_lock = NULL;
+       mtd->_unlock = NULL;
+       mtd->_suspend = nand_suspend;
+       mtd->_resume = nand_resume;
+       mtd->_block_isbad = nand_block_isbad;
+       mtd->_block_markbad = nand_block_markbad;
        mtd->writebufsize = mtd->writesize;
 
-       /* propagate ecc.layout to mtd_info */
+       /* propagate ecc info to mtd_info */
        mtd->ecclayout = chip->ecc.layout;
+       mtd->ecc_strength = chip->ecc.strength * chip->ecc.steps;
 
        /* Check, if we should skip the bad block table scan */
        if (chip->options & NAND_SKIP_BBTSCAN)
index ec688548c880ef67d1cf568e6f568c678099232f..2b6f632cf27429ff691c7007a8a842689a8878d4 100644 (file)
@@ -179,6 +179,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
        chip->ecc.mode = NAND_ECC_HW;
        chip->ecc.size = 256;
        chip->ecc.bytes = 3;
+       chip->ecc.strength = 1;
        chip->priv = ndfc;
 
        ndfc->mtd.priv = chip;
index b3a883e2a22f63942f83e245304f2de82fcdf116..c2b0bba9d8b39607626f091d398a3fccfa8687c4 100644 (file)
@@ -1058,6 +1058,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
                (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) {
                info->nand.ecc.bytes            = 3;
                info->nand.ecc.size             = 512;
+               info->nand.ecc.strength         = 1;
                info->nand.ecc.calculate        = omap_calculate_ecc;
                info->nand.ecc.hwctl            = omap_enable_hwecc;
                info->nand.ecc.correct          = omap_correct_data;
@@ -1101,8 +1102,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
                goto out_release_mem_region;
        }
 
-       mtd_device_parse_register(&info->mtd, NULL, 0,
-                       pdata->parts, pdata->nr_parts);
+       mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts,
+                                 pdata->nr_parts);
 
        platform_set_drvdata(pdev, &info->mtd);
 
index 29f505adaf8411581fcbb8d78d09935bef07f36d..1d3bfb26080cc27c4ef322271c409b49a58ee70d 100644 (file)
@@ -129,8 +129,8 @@ static int __init orion_nand_probe(struct platform_device *pdev)
        }
 
        mtd->name = "orion_nand";
-       ret = mtd_device_parse_register(mtd, NULL, 0,
-                       board->parts, board->nr_parts);
+       ret = mtd_device_parse_register(mtd, NULL, NULL, board->parts,
+                                       board->nr_parts);
        if (ret) {
                nand_release(mtd);
                goto no_dev;
index 7f2da6953357f80f06bfab9404d411db53f6ef05..6404e6e81b101ad12b405a1cb1d689273b30c447 100644 (file)
@@ -99,8 +99,9 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
        }
 
        err = mtd_device_parse_register(&data->mtd,
-                       pdata->chip.part_probe_types, 0,
-                       pdata->chip.partitions, pdata->chip.nr_partitions);
+                                       pdata->chip.part_probe_types, NULL,
+                                       pdata->chip.partitions,
+                                       pdata->chip.nr_partitions);
 
        if (!err)
                return err;
index 7e52af51a1986a419e66501bc3286c1e53397651..0ddd90e5788f4faf189f34af5f2f2a0b4e5b9608 100644 (file)
@@ -275,11 +275,10 @@ static int __init ppchameleonevb_init(void)
        ppchameleon_mtd->name = "ppchameleon-nand";
 
        /* Register the partitions */
-       mtd_device_parse_register(ppchameleon_mtd, NULL, 0,
-                       ppchameleon_mtd->size == NAND_SMALL_SIZE ?
-                               partition_info_me :
-                               partition_info_hi,
-                       NUM_PARTITIONS);
+       mtd_device_parse_register(ppchameleon_mtd, NULL, NULL,
+                                 ppchameleon_mtd->size == NAND_SMALL_SIZE ?
+                                       partition_info_me : partition_info_hi,
+                                 NUM_PARTITIONS);
 
  nand_evb_init:
        /****************************
@@ -365,11 +364,10 @@ static int __init ppchameleonevb_init(void)
        ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
 
        /* Register the partitions */
-       mtd_device_parse_register(ppchameleonevb_mtd, NULL, 0,
-                       ppchameleon_mtd->size == NAND_SMALL_SIZE ?
-                               partition_info_me :
-                               partition_info_hi,
-                       NUM_PARTITIONS);
+       mtd_device_parse_register(ppchameleonevb_mtd, NULL, NULL,
+                                 ppchameleon_mtd->size == NAND_SMALL_SIZE ?
+                                 partition_info_me : partition_info_hi,
+                                 NUM_PARTITIONS);
 
        /* Return happy */
        return 0;
index 5c3d719c37e6645d70385c3b75b71cf5992bdf7c..def50caa6f84b259fd5ed23e702efcd5977317cb 100644 (file)
@@ -1002,6 +1002,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
 KEEP_CONFIG:
        chip->ecc.mode = NAND_ECC_HW;
        chip->ecc.size = host->page_size;
+       chip->ecc.strength = 1;
 
        chip->options = NAND_NO_AUTOINCR;
        chip->options |= NAND_NO_READRDY;
@@ -1228,8 +1229,9 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
                        continue;
                }
 
-               ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
-                               pdata->parts[cs], pdata->nr_parts[cs]);
+               ret = mtd_device_parse_register(info->host[cs]->mtd, NULL,
+                                               NULL, pdata->parts[cs],
+                                               pdata->nr_parts[cs]);
                if (!ret)
                        probe_success = 1;
        }
index 769a4e096b3c6b8fc3ef036148bf78a609f289a1..c2040187c813e0084e6dc7da8441c675bbe6cc3e 100644 (file)
@@ -891,6 +891,7 @@ int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
        chip->ecc.mode = NAND_ECC_HW_SYNDROME;
        chip->ecc.size = R852_DMA_LEN;
        chip->ecc.bytes = SM_OOB_SIZE;
+       chip->ecc.strength = 2;
        chip->ecc.hwctl = r852_ecc_hwctl;
        chip->ecc.calculate = r852_ecc_calculate;
        chip->ecc.correct = r852_ecc_correct;
index f309addc2fa0505137f83a7a389bcb74b4fff9e6..e55b5cfbe1457d1300524602ec00c11aecb78e3b 100644 (file)
@@ -527,6 +527,7 @@ static int __init rtc_from4_init(void)
        this->ecc.mode = NAND_ECC_HW_SYNDROME;
        this->ecc.size = 512;
        this->ecc.bytes = 8;
+       this->ecc.strength = 3;
        /* return the status of extra status and ECC checks */
        this->errstat = rtc_from4_errstat;
        /* set the nand_oobinfo to support FPGA H/W error detection */
index 868685db6712fa3c18b593b4e9a171707f68f228..91121f33f743a304213512edfe96d364dd0c0404 100644 (file)
@@ -751,8 +751,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
        if (set)
                mtd->mtd.name = set->name;
 
-       return mtd_device_parse_register(&mtd->mtd, NULL, 0,
-                       set->partitions, set->nr_partitions);
+       return mtd_device_parse_register(&mtd->mtd, NULL, NULL,
+                                        set->partitions, set->nr_partitions);
 }
 
 /**
@@ -823,6 +823,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
                chip->ecc.calculate = s3c2410_nand_calculate_ecc;
                chip->ecc.correct   = s3c2410_nand_correct_data;
                chip->ecc.mode      = NAND_ECC_HW;
+               chip->ecc.strength  = 1;
 
                switch (info->cpu_type) {
                case TYPE_S3C2410:
index 93b1f74321c2a9e7793b8a867a1a02af15638278..e9b2b260de3ae081bbeb2e04bbff4d61902003b3 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
 #include <linux/mtd/mtd.h>
@@ -283,7 +284,7 @@ static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
 static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
 {
        struct sh_flctl *flctl = mtd_to_flctl(mtd);
-       uint32_t flcmncr_val = readl(FLCMNCR(flctl)) & ~SEL_16BIT;
+       uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT;
        uint32_t flcmdcr_val, addr_len_bytes = 0;
 
        /* Set SNAND bit if page size is 2048byte */
@@ -303,6 +304,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
                break;
        case NAND_CMD_READ0:
        case NAND_CMD_READOOB:
+       case NAND_CMD_RNDOUT:
                addr_len_bytes = flctl->rw_ADRCNT;
                flcmdcr_val |= CDSRC_E;
                if (flctl->chip.options & NAND_BUSWIDTH_16)
@@ -320,6 +322,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
                break;
        case NAND_CMD_READID:
                flcmncr_val &= ~SNAND_E;
+               flcmdcr_val |= CDSRC_E;
                addr_len_bytes = ADRCNT_1;
                break;
        case NAND_CMD_STATUS:
@@ -513,6 +516,8 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
        struct sh_flctl *flctl = mtd_to_flctl(mtd);
        uint32_t read_cmd = 0;
 
+       pm_runtime_get_sync(&flctl->pdev->dev);
+
        flctl->read_bytes = 0;
        if (command != NAND_CMD_PAGEPROG)
                flctl->index = 0;
@@ -525,7 +530,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
                        execmd_read_page_sector(mtd, page_addr);
                        break;
                }
-               empty_fifo(flctl);
                if (flctl->page_size)
                        set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
                                | command);
@@ -547,7 +551,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
                        break;
                }
 
-               empty_fifo(flctl);
                if (flctl->page_size) {
                        set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
                                | NAND_CMD_READ0);
@@ -559,15 +562,35 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
                flctl->read_bytes = mtd->oobsize;
                goto read_normal_exit;
 
+       case NAND_CMD_RNDOUT:
+               if (flctl->hwecc)
+                       break;
+
+               if (flctl->page_size)
+                       set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8)
+                               | command);
+               else
+                       set_cmd_regs(mtd, command, command);
+
+               set_addr(mtd, column, 0);
+
+               flctl->read_bytes = mtd->writesize + mtd->oobsize - column;
+               goto read_normal_exit;
+
        case NAND_CMD_READID:
-               empty_fifo(flctl);
                set_cmd_regs(mtd, command, command);
-               set_addr(mtd, 0, 0);
 
-               flctl->read_bytes = 4;
+               /* READID is always performed using an 8-bit bus */
+               if (flctl->chip.options & NAND_BUSWIDTH_16)
+                       column <<= 1;
+               set_addr(mtd, column, 0);
+
+               flctl->read_bytes = 8;
                writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
+               empty_fifo(flctl);
                start_translation(flctl);
-               read_datareg(flctl, 0); /* read and end */
+               read_fiforeg(flctl, flctl->read_bytes, 0);
+               wait_completion(flctl);
                break;
 
        case NAND_CMD_ERASE1:
@@ -650,29 +673,55 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
        default:
                break;
        }
-       return;
+       goto runtime_exit;
 
 read_normal_exit:
        writel(flctl->read_bytes, FLDTCNTR(flctl));     /* set read size */
+       empty_fifo(flctl);
        start_translation(flctl);
        read_fiforeg(flctl, flctl->read_bytes, 0);
        wait_completion(flctl);
+runtime_exit:
+       pm_runtime_put_sync(&flctl->pdev->dev);
        return;
 }
 
 static void flctl_select_chip(struct mtd_info *mtd, int chipnr)
 {
        struct sh_flctl *flctl = mtd_to_flctl(mtd);
-       uint32_t flcmncr_val = readl(FLCMNCR(flctl));
+       int ret;
 
        switch (chipnr) {
        case -1:
-               flcmncr_val &= ~CE0_ENABLE;
-               writel(flcmncr_val, FLCMNCR(flctl));
+               flctl->flcmncr_base &= ~CE0_ENABLE;
+
+               pm_runtime_get_sync(&flctl->pdev->dev);
+               writel(flctl->flcmncr_base, FLCMNCR(flctl));
+
+               if (flctl->qos_request) {
+                       dev_pm_qos_remove_request(&flctl->pm_qos);
+                       flctl->qos_request = 0;
+               }
+
+               pm_runtime_put_sync(&flctl->pdev->dev);
                break;
        case 0:
-               flcmncr_val |= CE0_ENABLE;
-               writel(flcmncr_val, FLCMNCR(flctl));
+               flctl->flcmncr_base |= CE0_ENABLE;
+
+               if (!flctl->qos_request) {
+                       ret = dev_pm_qos_add_request(&flctl->pdev->dev,
+                                                       &flctl->pm_qos, 100);
+                       if (ret < 0)
+                               dev_err(&flctl->pdev->dev,
+                                       "PM QoS request failed: %d\n", ret);
+                       flctl->qos_request = 1;
+               }
+
+               if (flctl->holden) {
+                       pm_runtime_get_sync(&flctl->pdev->dev);
+                       writel(HOLDEN, FLHOLDCR(flctl));
+                       pm_runtime_put_sync(&flctl->pdev->dev);
+               }
                break;
        default:
                BUG();
@@ -730,11 +779,6 @@ static int flctl_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
        return 0;
 }
 
-static void flctl_register_init(struct sh_flctl *flctl, unsigned long val)
-{
-       writel(val, FLCMNCR(flctl));
-}
-
 static int flctl_chip_init_tail(struct mtd_info *mtd)
 {
        struct sh_flctl *flctl = mtd_to_flctl(mtd);
@@ -781,13 +825,13 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
 
                chip->ecc.size = 512;
                chip->ecc.bytes = 10;
+               chip->ecc.strength = 4;
                chip->ecc.read_page = flctl_read_page_hwecc;
                chip->ecc.write_page = flctl_write_page_hwecc;
                chip->ecc.mode = NAND_ECC_HW;
 
                /* 4 symbols ECC enabled */
-               writel(readl(FLCMNCR(flctl)) | _4ECCEN | ECCPOS2 | ECCPOS_02,
-                               FLCMNCR(flctl));
+               flctl->flcmncr_base |= _4ECCEN | ECCPOS2 | ECCPOS_02;
        } else {
                chip->ecc.mode = NAND_ECC_SOFT;
        }
@@ -819,13 +863,13 @@ static int __devinit flctl_probe(struct platform_device *pdev)
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(&pdev->dev, "failed to get I/O memory\n");
-               goto err;
+               goto err_iomap;
        }
 
        flctl->reg = ioremap(res->start, resource_size(res));
        if (flctl->reg == NULL) {
                dev_err(&pdev->dev, "failed to remap I/O memory\n");
-               goto err;
+               goto err_iomap;
        }
 
        platform_set_drvdata(pdev, flctl);
@@ -833,9 +877,9 @@ static int __devinit flctl_probe(struct platform_device *pdev)
        nand = &flctl->chip;
        flctl_mtd->priv = nand;
        flctl->pdev = pdev;
+       flctl->flcmncr_base = pdata->flcmncr_val;
        flctl->hwecc = pdata->has_hwecc;
-
-       flctl_register_init(flctl, pdata->flcmncr_val);
+       flctl->holden = pdata->use_holden;
 
        nand->options = NAND_NO_AUTOINCR;
 
@@ -855,23 +899,28 @@ static int __devinit flctl_probe(struct platform_device *pdev)
                nand->read_word = flctl_read_word;
        }
 
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_resume(&pdev->dev);
+
        ret = nand_scan_ident(flctl_mtd, 1, NULL);
        if (ret)
-               goto err;
+               goto err_chip;
 
        ret = flctl_chip_init_tail(flctl_mtd);
        if (ret)
-               goto err;
+               goto err_chip;
 
        ret = nand_scan_tail(flctl_mtd);
        if (ret)
-               goto err;
+               goto err_chip;
 
        mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
 
        return 0;
 
-err:
+err_chip:
+       pm_runtime_disable(&pdev->dev);
+err_iomap:
        kfree(flctl);
        return ret;
 }
@@ -881,6 +930,7 @@ static int __devexit flctl_remove(struct platform_device *pdev)
        struct sh_flctl *flctl = platform_get_drvdata(pdev);
 
        nand_release(&flctl->mtd);
+       pm_runtime_disable(&pdev->dev);
        kfree(flctl);
 
        return 0;
index b175c0fd8b9378009ecbcbf2ad6ad3cbf9b6f8a7..3421e3762a5a1ceda275b76f56386b29929d7b44 100644 (file)
@@ -167,6 +167,7 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev)
        this->ecc.mode = NAND_ECC_HW;
        this->ecc.size = 256;
        this->ecc.bytes = 3;
+       this->ecc.strength = 1;
        this->badblock_pattern = data->badblock_pattern;
        this->ecc.layout = data->ecc_layout;
        this->ecc.hwctl = sharpsl_nand_enable_hwecc;
@@ -181,8 +182,8 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev)
        /* Register the partitions */
        sharpsl->mtd.name = "sharpsl-nand";
 
-       err = mtd_device_parse_register(&sharpsl->mtd, NULL, 0,
-                       data->partitions, data->nr_partitions);
+       err = mtd_device_parse_register(&sharpsl->mtd, NULL, NULL,
+                                       data->partitions, data->nr_partitions);
        if (err)
                goto err_add;
 
index 6caa0cd9d6a7c23394d815516f321ae32ed97983..5aa518081c513454e0fedb70f907cdeffcbaecd2 100644 (file)
@@ -430,6 +430,7 @@ static int tmio_probe(struct platform_device *dev)
        nand_chip->ecc.mode = NAND_ECC_HW;
        nand_chip->ecc.size = 512;
        nand_chip->ecc.bytes = 6;
+       nand_chip->ecc.strength = 2;
        nand_chip->ecc.hwctl = tmio_nand_enable_hwecc;
        nand_chip->ecc.calculate = tmio_nand_calculate_ecc;
        nand_chip->ecc.correct = tmio_nand_correct_data;
@@ -456,9 +457,9 @@ static int tmio_probe(struct platform_device *dev)
                goto err_scan;
        }
        /* Register the partitions */
-       retval = mtd_device_parse_register(mtd, NULL, 0,
-                       data ? data->partition : NULL,
-                       data ? data->num_partitions : 0);
+       retval = mtd_device_parse_register(mtd, NULL, NULL,
+                                          data ? data->partition : NULL,
+                                          data ? data->num_partitions : 0);
        if (!retval)
                return retval;
 
index c7c4f1d11c77f2f9cb1a41700f58549377d5ea64..26398dcf21cfdccb606c923a7e0d96eefbd370de 100644 (file)
@@ -356,6 +356,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
                /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */
                chip->ecc.size = 256;
                chip->ecc.bytes = 3;
+               chip->ecc.strength = 1;
                chip->chip_delay = 100;
                chip->controller = &drvdata->hw_control;
 
@@ -386,7 +387,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
                }
                mtd->name = txx9_priv->mtdname;
 
-               mtd_device_parse_register(mtd, NULL, 0, NULL, 0);
+               mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
                drvdata->mtds[i] = mtd;
        }
 
index a75382aff5f68d17f419307b70eb4427f7c07507..c5f4ebf4b384404a47df44ec843046e6bb243b44 100644 (file)
@@ -56,13 +56,6 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        if (memcmp(mtd->name, "DiskOnChip", 10))
                return;
 
-       if (!mtd_can_have_bb(mtd)) {
-               printk(KERN_ERR
-"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
-"Please use the new diskonchip driver under the NAND subsystem.\n");
-               return;
-       }
-
        pr_debug("NFTL: add_mtd for %s\n", mtd->name);
 
        nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
index 0ccd5bff254459323827bd1f54b69d65b990c510..1c4f97c63e623e518367be0a178beec800e0ceb5 100644 (file)
@@ -70,9 +70,9 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev)
                goto out_iounmap;
        }
 
-       err = mtd_device_parse_register(&info->mtd, NULL, 0,
-                       pdata ? pdata->parts : NULL,
-                       pdata ? pdata->nr_parts : 0);
+       err = mtd_device_parse_register(&info->mtd, NULL, NULL,
+                                       pdata ? pdata->parts : NULL,
+                                       pdata ? pdata->nr_parts : 0);
 
        platform_set_drvdata(pdev, info);
 
index 7e9ea6852b671d3e1b09f1c259eee49be3ecb968..398a827838480a41b4dbd71d4df4ed4b2a9d6e93 100644 (file)
@@ -751,9 +751,9 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
        if ((r = onenand_scan(&c->mtd, 1)) < 0)
                goto err_release_regulator;
 
-       r = mtd_device_parse_register(&c->mtd, NULL, 0,
-                       pdata ? pdata->parts : NULL,
-                       pdata ? pdata->nr_parts : 0);
+       r = mtd_device_parse_register(&c->mtd, NULL, NULL,
+                                     pdata ? pdata->parts : NULL,
+                                     pdata ? pdata->nr_parts : 0);
        if (r)
                goto err_release_onenand;
 
index a061bc163da2e128171bdc9fce6d146e6163b411..b3ce12ef359e83777280aff2b6f9fa9d58cc4dc9 100644 (file)
@@ -1753,16 +1753,6 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
        pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
                        (int)len);
 
-       /* Initialize retlen, in case of early exit */
-       *retlen = 0;
-
-       /* Do not allow writes past end of device */
-       if (unlikely((to + len) > mtd->size)) {
-               printk(KERN_ERR "%s: Attempt write to past end of device\n",
-                       __func__);
-               return -EINVAL;
-       }
-
        /* Reject writes, which are not page aligned */
         if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
                printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
@@ -1890,13 +1880,6 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
        ops->retlen = 0;
        ops->oobretlen = 0;
 
-       /* Do not allow writes past end of device */
-       if (unlikely((to + len) > mtd->size)) {
-               printk(KERN_ERR "%s: Attempt write to past end of device\n",
-                       __func__);
-               return -EINVAL;
-       }
-
        /* Reject writes, which are not page aligned */
         if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
                printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
@@ -2493,12 +2476,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
                        (unsigned long long)instr->addr,
                        (unsigned long long)instr->len);
 
-       /* Do not allow erase past end of device */
-       if (unlikely((len + addr) > mtd->size)) {
-               printk(KERN_ERR "%s: Erase past end of device\n", __func__);
-               return -EINVAL;
-       }
-
        if (FLEXONENAND(this)) {
                /* Find the eraseregion of this address */
                int i = flexonenand_region(mtd, addr);
@@ -2525,8 +2502,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
                return -EINVAL;
        }
 
-       instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
-
        /* Grab the lock and see if the device is available */
        onenand_get_device(mtd, FL_ERASING);
 
@@ -4103,33 +4078,34 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        mtd->oobavail = this->ecclayout->oobavail;
 
        mtd->ecclayout = this->ecclayout;
+       mtd->ecc_strength = 1;
 
        /* Fill in remaining MTD driver data */
        mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->erase = onenand_erase;
-       mtd->point = NULL;
-       mtd->unpoint = NULL;
-       mtd->read = onenand_read;
-       mtd->write = onenand_write;
-       mtd->read_oob = onenand_read_oob;
-       mtd->write_oob = onenand_write_oob;
-       mtd->panic_write = onenand_panic_write;
+       mtd->_erase = onenand_erase;
+       mtd->_point = NULL;
+       mtd->_unpoint = NULL;
+       mtd->_read = onenand_read;
+       mtd->_write = onenand_write;
+       mtd->_read_oob = onenand_read_oob;
+       mtd->_write_oob = onenand_write_oob;
+       mtd->_panic_write = onenand_panic_write;
 #ifdef CONFIG_MTD_ONENAND_OTP
-       mtd->get_fact_prot_info = onenand_get_fact_prot_info;
-       mtd->read_fact_prot_reg = onenand_read_fact_prot_reg;
-       mtd->get_user_prot_info = onenand_get_user_prot_info;
-       mtd->read_user_prot_reg = onenand_read_user_prot_reg;
-       mtd->write_user_prot_reg = onenand_write_user_prot_reg;
-       mtd->lock_user_prot_reg = onenand_lock_user_prot_reg;
+       mtd->_get_fact_prot_info = onenand_get_fact_prot_info;
+       mtd->_read_fact_prot_reg = onenand_read_fact_prot_reg;
+       mtd->_get_user_prot_info = onenand_get_user_prot_info;
+       mtd->_read_user_prot_reg = onenand_read_user_prot_reg;
+       mtd->_write_user_prot_reg = onenand_write_user_prot_reg;
+       mtd->_lock_user_prot_reg = onenand_lock_user_prot_reg;
 #endif
-       mtd->sync = onenand_sync;
-       mtd->lock = onenand_lock;
-       mtd->unlock = onenand_unlock;
-       mtd->suspend = onenand_suspend;
-       mtd->resume = onenand_resume;
-       mtd->block_isbad = onenand_block_isbad;
-       mtd->block_markbad = onenand_block_markbad;
+       mtd->_sync = onenand_sync;
+       mtd->_lock = onenand_lock;
+       mtd->_unlock = onenand_unlock;
+       mtd->_suspend = onenand_suspend;
+       mtd->_resume = onenand_resume;
+       mtd->_block_isbad = onenand_block_isbad;
+       mtd->_block_markbad = onenand_block_markbad;
        mtd->owner = THIS_MODULE;
        mtd->writebufsize = mtd->writesize;
 
index fa1ee43f735b5bb8ec97d5c6861525f8c4b5757c..8e4b3f2742ba1793f013e6680e3058f1a9e424f9 100644 (file)
@@ -923,7 +923,7 @@ static int s3c_onenand_probe(struct platform_device *pdev)
                r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
                if (!r) {
                        dev_err(&pdev->dev, "no buffer memory resource defined\n");
-                       return -ENOENT;
+                       err = -ENOENT;
                        goto ahb_resource_failed;
                }
 
@@ -964,7 +964,7 @@ static int s3c_onenand_probe(struct platform_device *pdev)
                r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
                if (!r) {
                        dev_err(&pdev->dev, "no dma memory resource defined\n");
-                       return -ENOENT;
+                       err = -ENOENT;
                        goto dma_resource_failed;
                }
 
@@ -1014,7 +1014,7 @@ static int s3c_onenand_probe(struct platform_device *pdev)
        if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ)
                dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n");
 
-       err = mtd_device_parse_register(mtd, NULL, 0,
+       err = mtd_device_parse_register(mtd, NULL, NULL,
                                        pdata ? pdata->parts : NULL,
                                        pdata ? pdata->nr_parts : 0);
 
index 48970c14beffd911fd154e53dd3b56690f03d4f9..580035c803d693eb38b8f37750a85d57f70cadfa 100644 (file)
@@ -78,8 +78,7 @@ static int parse_redboot_partitions(struct mtd_info *master,
 
        if ( directory < 0 ) {
                offset = master->size + directory * master->erasesize;
-               while (mtd_can_have_bb(master) &&
-                      mtd_block_isbad(master, offset)) {
+               while (mtd_block_isbad(master, offset)) {
                        if (!offset) {
                        nogood:
                                printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n");
@@ -89,8 +88,7 @@ static int parse_redboot_partitions(struct mtd_info *master,
                }
        } else {
                offset = directory * master->erasesize;
-               while (mtd_can_have_bb(master) &&
-                      mtd_block_isbad(master, offset)) {
+               while (mtd_block_isbad(master, offset)) {
                        offset += master->erasesize;
                        if (offset == master->size)
                                goto nogood;
index 072ed5970e2f3dacf5a1e5d654a9f9b54183d8e3..9e2dfd517aa51c68e15e80d864d1a12f80c1fbe8 100644 (file)
@@ -1256,7 +1256,7 @@ static void sm_remove_dev(struct mtd_blktrans_dev *dev)
 
 static struct mtd_blktrans_ops sm_ftl_ops = {
        .name           = "smblk",
-       .major          = -1,
+       .major          = 0,
        .part_bits      = SM_FTL_PARTN_BITS,
        .blksize        = SM_SECTOR_SIZE,
        .getgeo         = sm_getgeo,
index 941bc3c05d6e51232eabfbdd6db08a0248bb4328..90b98822d9a466a3266c1eba5dc12dd0e906f6e6 100644 (file)
@@ -174,11 +174,7 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
        int err = 0, lnum, offs, total_read;
        struct gluebi_device *gluebi;
 
-       if (len < 0 || from < 0 || from + len > mtd->size)
-               return -EINVAL;
-
        gluebi = container_of(mtd, struct gluebi_device, mtd);
-
        lnum = div_u64_rem(from, mtd->erasesize, &offs);
        total_read = len;
        while (total_read) {
@@ -218,14 +214,7 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
        int err = 0, lnum, offs, total_written;
        struct gluebi_device *gluebi;
 
-       if (len < 0 || to < 0 || len + to > mtd->size)
-               return -EINVAL;
-
        gluebi = container_of(mtd, struct gluebi_device, mtd);
-
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-
        lnum = div_u64_rem(to, mtd->erasesize, &offs);
 
        if (len % mtd->writesize || offs % mtd->writesize)
@@ -265,21 +254,13 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
        int err, i, lnum, count;
        struct gluebi_device *gluebi;
 
-       if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize)
-               return -EINVAL;
-       if (instr->len < 0 || instr->addr + instr->len > mtd->size)
-               return -EINVAL;
        if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd))
                return -EINVAL;
 
        lnum = mtd_div_by_eb(instr->addr, mtd);
        count = mtd_div_by_eb(instr->len, mtd);
-
        gluebi = container_of(mtd, struct gluebi_device, mtd);
 
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-
        for (i = 0; i < count - 1; i++) {
                err = ubi_leb_unmap(gluebi->desc, lnum + i);
                if (err)
@@ -340,11 +321,11 @@ static int gluebi_create(struct ubi_device_info *di,
        mtd->owner      = THIS_MODULE;
        mtd->writesize  = di->min_io_size;
        mtd->erasesize  = vi->usable_leb_size;
-       mtd->read       = gluebi_read;
-       mtd->write      = gluebi_write;
-       mtd->erase      = gluebi_erase;
-       mtd->get_device = gluebi_get_device;
-       mtd->put_device = gluebi_put_device;
+       mtd->_read       = gluebi_read;
+       mtd->_write      = gluebi_write;
+       mtd->_erase      = gluebi_erase;
+       mtd->_get_device = gluebi_get_device;
+       mtd->_put_device = gluebi_put_device;
 
        /*
         * In case of dynamic a volume, MTD device size is just volume size. In
index 26b3c23b0b6f263a72678675eadb8ff673be5f2c..758148379b0e19d34e0e413884649060efa9090f 100644 (file)
@@ -193,7 +193,7 @@ static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
                erase->state = MTD_ERASE_DONE;
        } else {
                erase->state = MTD_ERASE_FAILED;
-               erase->fail_addr = 0xffffffff;
+               erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
        }
        mtd_erase_callback(erase);
        return rc;
@@ -263,10 +263,10 @@ static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd)
                part->mtd.owner = THIS_MODULE;
                part->mtd.priv = efx_mtd;
                part->mtd.name = part->name;
-               part->mtd.erase = efx_mtd_erase;
-               part->mtd.read = efx_mtd->ops->read;
-               part->mtd.write = efx_mtd->ops->write;
-               part->mtd.sync = efx_mtd_sync;
+               part->mtd._erase = efx_mtd_erase;
+               part->mtd._read = efx_mtd->ops->read;
+               part->mtd._write = efx_mtd->ops->write;
+               part->mtd._sync = efx_mtd_sync;
 
                if (mtd_device_register(&part->mtd, NULL, 0))
                        goto fail;
index 423eb26386c87c7594b460cf18f2589254184e0d..d70ede7a7f96bd98f1e5175761d3c598086fe72d 100644 (file)
@@ -290,8 +290,8 @@ config FARSYNC
          Frame Relay or X.25/LAPB.
 
          If you want the module to be automatically loaded when the interface
-         is referenced then you should add "alias hdlcX farsync" to
-         /etc/modprobe.conf for each interface, where X is 0, 1, 2, ..., or
+         is referenced then you should add "alias hdlcX farsync" to a file
+         in /etc/modprobe.d/ for each interface, where X is 0, 1, 2, ..., or
          simply use "alias hdlc* farsync" to indicate all of them.
 
          To compile this driver as a module, choose M here: the
index 060fd22a1103856988d6866b64c4d8e8e3e95a89..0f150f271c2aa4040c8fcb9905783c99cb8b4e7a 100644 (file)
@@ -277,40 +277,6 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
        return 0;
 }
 
-/**
- * acpi_dev_run_wake - Enable/disable wake-up for given device.
- * @phys_dev: Device to enable/disable the platform to wake-up the system for.
- * @enable: Whether enable or disable the wake-up functionality.
- *
- * Find the ACPI device object corresponding to @pci_dev and try to
- * enable/disable the GPE associated with it.
- */
-static int acpi_dev_run_wake(struct device *phys_dev, bool enable)
-{
-       struct acpi_device *dev;
-       acpi_handle handle;
-
-       if (!device_run_wake(phys_dev))
-               return -EINVAL;
-
-       handle = DEVICE_ACPI_HANDLE(phys_dev);
-       if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) {
-               dev_dbg(phys_dev, "ACPI handle has no context in %s!\n",
-                       __func__);
-               return -ENODEV;
-       }
-
-       if (enable) {
-               acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0);
-               acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
-       } else {
-               acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
-               acpi_disable_wakeup_device_power(dev);
-       }
-
-       return 0;
-}
-
 static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)
 {
        while (bus->parent) {
@@ -318,14 +284,14 @@ static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)
 
                if (bridge->pme_interrupt)
                        return;
-               if (!acpi_dev_run_wake(&bridge->dev, enable))
+               if (!acpi_pm_device_run_wake(&bridge->dev, enable))
                        return;
                bus = bus->parent;
        }
 
        /* We have reached the root bus. */
        if (bus->bridge)
-               acpi_dev_run_wake(bus->bridge, enable);
+               acpi_pm_device_run_wake(bus->bridge, enable);
 }
 
 static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
@@ -333,7 +299,7 @@ static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
        if (dev->pme_interrupt)
                return 0;
 
-       if (!acpi_dev_run_wake(&dev->dev, enable))
+       if (!acpi_pm_device_run_wake(&dev->dev, enable))
                return 0;
 
        acpi_pci_propagate_run_wake(dev->bus, enable);
index 4bdef24cd412ff75db214f0461ab98e908adccda..b500840a143b08ac72e8c045fea66135535676d5 100644 (file)
@@ -508,9 +508,6 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
        int pos;
        u32 reg32;
 
-       if (aspm_disabled)
-               return 0;
-
        /*
         * Some functions in a slot might not all be PCIe functions,
         * very strange. Disable ASPM for the whole slot
@@ -519,6 +516,16 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
                pos = pci_pcie_cap(child);
                if (!pos)
                        return -EINVAL;
+
+               /*
+                * If ASPM is disabled then we're not going to change
+                * the BIOS state. It's safe to continue even if it's a
+                * pre-1.1 device
+                */
+
+               if (aspm_disabled)
+                       continue;
+
                /*
                 * Disable ASPM for pre-1.1 PCIe device, we follow MS to use
                 * RBER bit to determine if a function is 1.1 version device
index 88a98cff5a44a04e2d69771cc78ec7de3edbce82..f7ba316e0ed612f7adddafd75fe7f94b13d78656 100644 (file)
@@ -609,25 +609,16 @@ static bool mcp_exceeded(struct ips_driver *ips)
        bool ret = false;
        u32 temp_limit;
        u32 avg_power;
-       const char *msg = "MCP limit exceeded: ";
 
        spin_lock_irqsave(&ips->turbo_status_lock, flags);
 
        temp_limit = ips->mcp_temp_limit * 100;
-       if (ips->mcp_avg_temp > temp_limit) {
-               dev_info(&ips->dev->dev,
-                       "%sAvg temp %u, limit %u\n", msg, ips->mcp_avg_temp,
-                       temp_limit);
+       if (ips->mcp_avg_temp > temp_limit)
                ret = true;
-       }
 
        avg_power = ips->cpu_avg_power + ips->mch_avg_power;
-       if (avg_power > ips->mcp_power_limit) {
-               dev_info(&ips->dev->dev,
-                       "%sAvg power %u, limit %u\n", msg, avg_power,
-                       ips->mcp_power_limit);
+       if (avg_power > ips->mcp_power_limit)
                ret = true;
-       }
 
        spin_unlock_irqrestore(&ips->turbo_status_lock, flags);
 
index b00c17612a89441752938de2c424d51eec6c941b..d21e8f59c84e2f8e1b500bfdf433a26afe49ac6c 100644 (file)
@@ -321,9 +321,14 @@ static int __init acpi_pnp_match(struct device *dev, void *_pnp)
 {
        struct acpi_device *acpi = to_acpi_device(dev);
        struct pnp_dev *pnp = _pnp;
+       struct device *physical_device;
+
+       physical_device = acpi_get_physical_device(acpi->handle);
+       if (physical_device)
+               put_device(physical_device);
 
        /* true means it matched */
-       return !acpi_get_physical_device(acpi->handle)
+       return !physical_device
            && compare_pnp_id(pnp->id, acpi_device_hid(acpi));
 }
 
index 459f66437fe93159bb696efad3cb3e093eea6dfa..99dc29f2f2f2ba84b16430a51548817b094b9c3a 100644 (file)
@@ -249,7 +249,7 @@ config CHARGER_TWL4030
          Say Y here to enable support for TWL4030 Battery Charge Interface.
 
 config CHARGER_LP8727
-       tristate "National Semiconductor LP8727 charger driver"
+       tristate "TI/National Semiconductor LP8727 charger driver"
        depends on I2C
        help
          Say Y here to enable support for LP8727 Charger Driver.
@@ -288,4 +288,23 @@ config CHARGER_MAX8998
          Say Y to enable support for the battery charger control sysfs and
          platform data of MAX8998/LP3974 PMICs.
 
+config CHARGER_SMB347
+       tristate "Summit Microelectronics SMB347 Battery Charger"
+       depends on I2C
+       help
+         Say Y to include support for Summit Microelectronics SMB347
+         Battery Charger.
+
+config AB8500_BM
+       bool "AB8500 Battery Management Driver"
+       depends on AB8500_CORE && AB8500_GPADC
+       help
+         Say Y to include support for AB5500 battery management.
+
+config AB8500_BATTERY_THERM_ON_BATCTRL
+       bool "Thermistor connected on BATCTRL ADC"
+       depends on AB8500_BM
+       help
+         Say Y to enable battery temperature measurements using
+         thermistor connected on BATCTRL ADC.
 endif # POWER_SUPPLY
index c590fa5334066b398213e2e1e0850a4eb27335cb..b6b243416c0ee1947a47efd85b205341dc0f1463 100644 (file)
@@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
 obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
 obj-$(CONFIG_BATTERY_JZ4740)   += jz4740-battery.o
 obj-$(CONFIG_BATTERY_INTEL_MID)        += intel_mid_battery.o
+obj-$(CONFIG_AB8500_BM)                += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o
 obj-$(CONFIG_CHARGER_ISP1704)  += isp1704_charger.o
 obj-$(CONFIG_CHARGER_MAX8903)  += max8903_charger.o
 obj-$(CONFIG_CHARGER_TWL4030)  += twl4030_charger.o
@@ -42,3 +43,4 @@ obj-$(CONFIG_CHARGER_GPIO)    += gpio-charger.o
 obj-$(CONFIG_CHARGER_MANAGER)  += charger-manager.o
 obj-$(CONFIG_CHARGER_MAX8997)  += max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)  += max8998_charger.o
+obj-$(CONFIG_CHARGER_SMB347)   += smb347-charger.o
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
new file mode 100644 (file)
index 0000000..d8bb993
--- /dev/null
@@ -0,0 +1,1124 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Battery temperature driver for AB8500
+ *
+ * License Terms: GNU General Public License v2
+ * Author:
+ *     Johan Palsson <johan.palsson@stericsson.com>
+ *     Karl Komierowski <karl.komierowski@stericsson.com>
+ *     Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/jiffies.h>
+
+#define VTVOUT_V                       1800
+
+#define BTEMP_THERMAL_LOW_LIMIT                -10
+#define BTEMP_THERMAL_MED_LIMIT                0
+#define BTEMP_THERMAL_HIGH_LIMIT_52    52
+#define BTEMP_THERMAL_HIGH_LIMIT_57    57
+#define BTEMP_THERMAL_HIGH_LIMIT_62    62
+
+#define BTEMP_BATCTRL_CURR_SRC_7UA     7
+#define BTEMP_BATCTRL_CURR_SRC_20UA    20
+
+#define to_ab8500_btemp_device_info(x) container_of((x), \
+       struct ab8500_btemp, btemp_psy);
+
+/**
+ * struct ab8500_btemp_interrupts - ab8500 interrupts
+ * @name:      name of the interrupt
+ * @isr                function pointer to the isr
+ */
+struct ab8500_btemp_interrupts {
+       char *name;
+       irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab8500_btemp_events {
+       bool batt_rem;
+       bool btemp_high;
+       bool btemp_medhigh;
+       bool btemp_lowmed;
+       bool btemp_low;
+       bool ac_conn;
+       bool usb_conn;
+};
+
+struct ab8500_btemp_ranges {
+       int btemp_high_limit;
+       int btemp_med_limit;
+       int btemp_low_limit;
+};
+
+/**
+ * struct ab8500_btemp - ab8500 BTEMP device information
+ * @dev:               Pointer to the structure device
+ * @node:              List of AB8500 BTEMPs, hence prepared for reentrance
+ * @curr_source:       What current source we use, in uA
+ * @bat_temp:          Battery temperature in degree Celcius
+ * @prev_bat_temp      Last dispatched battery temperature
+ * @parent:            Pointer to the struct ab8500
+ * @gpadc:             Pointer to the struct gpadc
+ * @fg:                        Pointer to the struct fg
+ * @pdata:             Pointer to the abx500_btemp platform data
+ * @bat:               Pointer to the abx500_bm platform data
+ * @btemp_psy:         Structure for BTEMP specific battery properties
+ * @events:            Structure for information about events triggered
+ * @btemp_ranges:      Battery temperature range structure
+ * @btemp_wq:          Work queue for measuring the temperature periodically
+ * @btemp_periodic_work:       Work for measuring the temperature periodically
+ */
+struct ab8500_btemp {
+       struct device *dev;
+       struct list_head node;
+       int curr_source;
+       int bat_temp;
+       int prev_bat_temp;
+       struct ab8500 *parent;
+       struct ab8500_gpadc *gpadc;
+       struct ab8500_fg *fg;
+       struct abx500_btemp_platform_data *pdata;
+       struct abx500_bm_data *bat;
+       struct power_supply btemp_psy;
+       struct ab8500_btemp_events events;
+       struct ab8500_btemp_ranges btemp_ranges;
+       struct workqueue_struct *btemp_wq;
+       struct delayed_work btemp_periodic_work;
+};
+
+/* BTEMP power supply properties */
+static enum power_supply_property ab8500_btemp_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static LIST_HEAD(ab8500_btemp_list);
+
+/**
+ * ab8500_btemp_get() - returns a reference to the primary AB8500 BTEMP
+ * (i.e. the first BTEMP in the instance list)
+ */
+struct ab8500_btemp *ab8500_btemp_get(void)
+{
+       struct ab8500_btemp *btemp;
+       btemp = list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
+
+       return btemp;
+}
+
+/**
+ * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance
+ * @di:                pointer to the ab8500_btemp structure
+ * @v_batctrl: measured batctrl voltage
+ * @inst_curr: measured instant current
+ *
+ * This function returns the battery resistance that is
+ * derived from the BATCTRL voltage.
+ * Returns value in Ohms.
+ */
+static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
+       int v_batctrl, int inst_curr)
+{
+       int rbs;
+
+       if (is_ab8500_1p1_or_earlier(di->parent)) {
+               /*
+                * For ABB cut1.0 and 1.1 BAT_CTRL is internally
+                * connected to 1.8V through a 450k resistor
+                */
+               return (450000 * (v_batctrl)) / (1800 - v_batctrl);
+       }
+
+       if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL) {
+               /*
+                * If the battery has internal NTC, we use the current
+                * source to calculate the resistance, 7uA or 20uA
+                */
+               rbs = (v_batctrl * 1000
+                      - di->bat->gnd_lift_resistance * inst_curr)
+                     / di->curr_source;
+       } else {
+               /*
+                * BAT_CTRL is internally
+                * connected to 1.8V through a 80k resistor
+                */
+               rbs = (80000 * (v_batctrl)) / (1800 - v_batctrl);
+       }
+
+       return rbs;
+}
+
+/**
+ * ab8500_btemp_read_batctrl_voltage() - measure batctrl voltage
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * This function returns the voltage on BATCTRL. Returns value in mV.
+ */
+static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di)
+{
+       int vbtemp;
+       static int prev;
+
+       vbtemp = ab8500_gpadc_convert(di->gpadc, BAT_CTRL);
+       if (vbtemp < 0) {
+               dev_err(di->dev,
+                       "%s gpadc conversion failed, using previous value",
+                       __func__);
+               return prev;
+       }
+       prev = vbtemp;
+       return vbtemp;
+}
+
+/**
+ * ab8500_btemp_curr_source_enable() - enable/disable batctrl current source
+ * @di:                pointer to the ab8500_btemp structure
+ * @enable:    enable or disable the current source
+ *
+ * Enable or disable the current sources for the BatCtrl AD channel
+ */
+static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
+       bool enable)
+{
+       int curr;
+       int ret = 0;
+
+       /*
+        * BATCTRL current sources are included on AB8500 cut2.0
+        * and future versions
+        */
+       if (is_ab8500_1p1_or_earlier(di->parent))
+               return 0;
+
+       /* Only do this for batteries with internal NTC */
+       if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
+               if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
+                       curr = BAT_CTRL_7U_ENA;
+               else
+                       curr = BAT_CTRL_20U_ENA;
+
+               dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source);
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       FORCE_BAT_CTRL_CMP_HIGH, FORCE_BAT_CTRL_CMP_HIGH);
+               if (ret) {
+                       dev_err(di->dev, "%s failed setting cmp_force\n",
+                               __func__);
+                       return ret;
+               }
+
+               /*
+                * We have to wait one 32kHz cycle before enabling
+                * the current source, since ForceBatCtrlCmpHigh needs
+                * to be written in a separate cycle
+                */
+               udelay(32);
+
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       FORCE_BAT_CTRL_CMP_HIGH | curr);
+               if (ret) {
+                       dev_err(di->dev, "%s failed enabling current source\n",
+                               __func__);
+                       goto disable_curr_source;
+               }
+       } else if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
+               dev_dbg(di->dev, "Disable BATCTRL curr source\n");
+
+               /* Write 0 to the curr bits */
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+                       ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+               if (ret) {
+                       dev_err(di->dev, "%s failed disabling current source\n",
+                               __func__);
+                       goto disable_curr_source;
+               }
+
+               /* Enable Pull-Up and comparator */
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
+                       BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
+               if (ret) {
+                       dev_err(di->dev, "%s failed enabling PU and comp\n",
+                               __func__);
+                       goto enable_pu_comp;
+               }
+
+               /*
+                * We have to wait one 32kHz cycle before disabling
+                * ForceBatCtrlCmpHigh since this needs to be written
+                * in a separate cycle
+                */
+               udelay(32);
+
+               /* Disable 'force comparator' */
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
+               if (ret) {
+                       dev_err(di->dev, "%s failed disabling force comp\n",
+                               __func__);
+                       goto disable_force_comp;
+               }
+       }
+       return ret;
+
+       /*
+        * We have to try unsetting FORCE_BAT_CTRL_CMP_HIGH one more time
+        * if we got an error above
+        */
+disable_curr_source:
+       /* Write 0 to the curr bits */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+                       ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+       if (ret) {
+               dev_err(di->dev, "%s failed disabling current source\n",
+                       __func__);
+               return ret;
+       }
+enable_pu_comp:
+       /* Enable Pull-Up and comparator */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+               BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
+               BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
+       if (ret) {
+               dev_err(di->dev, "%s failed enabling PU and comp\n",
+                       __func__);
+               return ret;
+       }
+
+disable_force_comp:
+       /*
+        * We have to wait one 32kHz cycle before disabling
+        * ForceBatCtrlCmpHigh since this needs to be written
+        * in a separate cycle
+        */
+       udelay(32);
+
+       /* Disable 'force comparator' */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+               FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
+       if (ret) {
+               dev_err(di->dev, "%s failed disabling force comp\n",
+                       __func__);
+               return ret;
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_btemp_get_batctrl_res() - get battery resistance
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * This function returns the battery pack identification resistance.
+ * Returns value in Ohms.
+ */
+static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
+{
+       int ret;
+       int batctrl = 0;
+       int res;
+       int inst_curr;
+       int i;
+
+       /*
+        * BATCTRL current sources are included on AB8500 cut2.0
+        * and future versions
+        */
+       ret = ab8500_btemp_curr_source_enable(di, true);
+       if (ret) {
+               dev_err(di->dev, "%s curr source enabled failed\n", __func__);
+               return ret;
+       }
+
+       if (!di->fg)
+               di->fg = ab8500_fg_get();
+       if (!di->fg) {
+               dev_err(di->dev, "No fg found\n");
+               return -EINVAL;
+       }
+
+       ret = ab8500_fg_inst_curr_start(di->fg);
+
+       if (ret) {
+               dev_err(di->dev, "Failed to start current measurement\n");
+               return ret;
+       }
+
+       /*
+        * Since there is no interrupt when current measurement is done,
+        * loop for over 250ms (250ms is one sample conversion time
+        * with 32.768 Khz RTC clock). Note that a stop time must be set
+        * since the ab8500_btemp_read_batctrl_voltage call can block and
+        * take an unknown amount of time to complete.
+        */
+       i = 0;
+
+       do {
+               batctrl += ab8500_btemp_read_batctrl_voltage(di);
+               i++;
+               msleep(20);
+       } while (!ab8500_fg_inst_curr_done(di->fg));
+       batctrl /= i;
+
+       ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr);
+       if (ret) {
+               dev_err(di->dev, "Failed to finalize current measurement\n");
+               return ret;
+       }
+
+       res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr);
+
+       ret = ab8500_btemp_curr_source_enable(di, false);
+       if (ret) {
+               dev_err(di->dev, "%s curr source disable failed\n", __func__);
+               return ret;
+       }
+
+       dev_dbg(di->dev, "%s batctrl: %d res: %d inst_curr: %d samples: %d\n",
+               __func__, batctrl, res, inst_curr, i);
+
+       return res;
+}
+
+/**
+ * ab8500_btemp_res_to_temp() - resistance to temperature
+ * @di:                pointer to the ab8500_btemp structure
+ * @tbl:       pointer to the resiatance to temperature table
+ * @tbl_size:  size of the resistance to temperature table
+ * @res:       resistance to calculate the temperature from
+ *
+ * This function returns the battery temperature in degrees Celcius
+ * based on the NTC resistance.
+ */
+static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
+       const struct abx500_res_to_temp *tbl, int tbl_size, int res)
+{
+       int i, temp;
+       /*
+        * Calculate the formula for the straight line
+        * Simple interpolation if we are within
+        * the resistance table limits, extrapolate
+        * if resistance is outside the limits.
+        */
+       if (res > tbl[0].resist)
+               i = 0;
+       else if (res <= tbl[tbl_size - 1].resist)
+               i = tbl_size - 2;
+       else {
+               i = 0;
+               while (!(res <= tbl[i].resist &&
+                       res > tbl[i + 1].resist))
+                       i++;
+       }
+
+       temp = tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
+               (res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
+       return temp;
+}
+
+/**
+ * ab8500_btemp_measure_temp() - measure battery temperature
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * Returns battery temperature (on success) else the previous temperature
+ */
+static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
+{
+       int temp;
+       static int prev;
+       int rbat, rntc, vntc;
+       u8 id;
+
+       id = di->bat->batt_id;
+
+       if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+                       id != BATTERY_UNKNOWN) {
+
+               rbat = ab8500_btemp_get_batctrl_res(di);
+               if (rbat < 0) {
+                       dev_err(di->dev, "%s get batctrl res failed\n",
+                               __func__);
+                       /*
+                        * Return out-of-range temperature so that
+                        * charging is stopped
+                        */
+                       return BTEMP_THERMAL_LOW_LIMIT;
+               }
+
+               temp = ab8500_btemp_res_to_temp(di,
+                       di->bat->bat_type[id].r_to_t_tbl,
+                       di->bat->bat_type[id].n_temp_tbl_elements, rbat);
+       } else {
+               vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL);
+               if (vntc < 0) {
+                       dev_err(di->dev,
+                               "%s gpadc conversion failed,"
+                               " using previous value\n", __func__);
+                       return prev;
+               }
+               /*
+                * The PCB NTC is sourced from VTVOUT via a 230kOhm
+                * resistor.
+                */
+               rntc = 230000 * vntc / (VTVOUT_V - vntc);
+
+               temp = ab8500_btemp_res_to_temp(di,
+                       di->bat->bat_type[id].r_to_t_tbl,
+                       di->bat->bat_type[id].n_temp_tbl_elements, rntc);
+               prev = temp;
+       }
+       dev_dbg(di->dev, "Battery temperature is %d\n", temp);
+       return temp;
+}
+
+/**
+ * ab8500_btemp_id() - Identify the connected battery
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * This function will try to identify the battery by reading the ID
+ * resistor. Some brands use a combined ID resistor with a NTC resistor to
+ * both be able to identify and to read the temperature of it.
+ */
+static int ab8500_btemp_id(struct ab8500_btemp *di)
+{
+       int res;
+       u8 i;
+
+       di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
+       di->bat->batt_id = BATTERY_UNKNOWN;
+
+       res =  ab8500_btemp_get_batctrl_res(di);
+       if (res < 0) {
+               dev_err(di->dev, "%s get batctrl res failed\n", __func__);
+               return -ENXIO;
+       }
+
+       /* BATTERY_UNKNOWN is defined on position 0, skip it! */
+       for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) {
+               if ((res <= di->bat->bat_type[i].resis_high) &&
+                       (res >= di->bat->bat_type[i].resis_low)) {
+                       dev_dbg(di->dev, "Battery detected on %s"
+                               " low %d < res %d < high: %d"
+                               " index: %d\n",
+                               di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL ?
+                               "BATCTRL" : "BATTEMP",
+                               di->bat->bat_type[i].resis_low, res,
+                               di->bat->bat_type[i].resis_high, i);
+
+                       di->bat->batt_id = i;
+                       break;
+               }
+       }
+
+       if (di->bat->batt_id == BATTERY_UNKNOWN) {
+               dev_warn(di->dev, "Battery identified as unknown"
+                       ", resistance %d Ohm\n", res);
+               return -ENXIO;
+       }
+
+       /*
+        * We only have to change current source if the
+        * detected type is Type 1, else we use the 7uA source
+        */
+       if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+                       di->bat->batt_id == 1) {
+               dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
+               di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+       }
+
+       return di->bat->batt_id;
+}
+
+/**
+ * ab8500_btemp_periodic_work() - Measuring the temperature periodically
+ * @work:      pointer to the work_struct structure
+ *
+ * Work function for measuring the temperature periodically
+ */
+static void ab8500_btemp_periodic_work(struct work_struct *work)
+{
+       int interval;
+       struct ab8500_btemp *di = container_of(work,
+               struct ab8500_btemp, btemp_periodic_work.work);
+
+       di->bat_temp = ab8500_btemp_measure_temp(di);
+
+       if (di->bat_temp != di->prev_bat_temp) {
+               di->prev_bat_temp = di->bat_temp;
+               power_supply_changed(&di->btemp_psy);
+       }
+
+       if (di->events.ac_conn || di->events.usb_conn)
+               interval = di->bat->temp_interval_chg;
+       else
+               interval = di->bat->temp_interval_nochg;
+
+       /* Schedule a new measurement */
+       queue_delayed_work(di->btemp_wq,
+               &di->btemp_periodic_work,
+               round_jiffies(interval * HZ));
+}
+
+/**
+ * ab8500_btemp_batctrlindb_handler() - battery removal detected
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+       dev_err(di->dev, "Battery removal detected!\n");
+
+       di->events.batt_rem = true;
+       power_supply_changed(&di->btemp_psy);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_templow_handler() - battery temp lower than 10 degrees
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+
+       if (is_ab8500_2p0_or_earlier(di->parent)) {
+               dev_dbg(di->dev, "Ignore false btemp low irq"
+                       " for ABB cut 1.0, 1.1 and 2.0\n");
+       } else {
+               dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
+
+               di->events.btemp_low = true;
+               di->events.btemp_high = false;
+               di->events.btemp_medhigh = false;
+               di->events.btemp_lowmed = false;
+               power_supply_changed(&di->btemp_psy);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_temphigh_handler() - battery temp higher than max temp
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+
+       dev_crit(di->dev, "Battery temperature is higher than MAX temp\n");
+
+       di->events.btemp_high = true;
+       di->events.btemp_medhigh = false;
+       di->events.btemp_lowmed = false;
+       di->events.btemp_low = false;
+       power_supply_changed(&di->btemp_psy);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_lowmed_handler() - battery temp between low and medium
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+
+       dev_dbg(di->dev, "Battery temperature is between low and medium\n");
+
+       di->events.btemp_lowmed = true;
+       di->events.btemp_medhigh = false;
+       di->events.btemp_high = false;
+       di->events.btemp_low = false;
+       power_supply_changed(&di->btemp_psy);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_medhigh_handler() - battery temp between medium and high
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+
+       dev_dbg(di->dev, "Battery temperature is between medium and high\n");
+
+       di->events.btemp_medhigh = true;
+       di->events.btemp_lowmed = false;
+       di->events.btemp_high = false;
+       di->events.btemp_low = false;
+       power_supply_changed(&di->btemp_psy);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_periodic() - Periodic temperature measurements
+ * @di:                pointer to the ab8500_btemp structure
+ * @enable:    enable or disable periodic temperature measurements
+ *
+ * Starts of stops periodic temperature measurements. Periodic measurements
+ * should only be done when a charger is connected.
+ */
+static void ab8500_btemp_periodic(struct ab8500_btemp *di,
+       bool enable)
+{
+       dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n",
+               enable);
+       /*
+        * Make sure a new measurement is done directly by cancelling
+        * any pending work
+        */
+       cancel_delayed_work_sync(&di->btemp_periodic_work);
+
+       if (enable)
+               queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);
+}
+
+/**
+ * ab8500_btemp_get_temp() - get battery temperature
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * Returns battery temperature
+ */
+static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
+{
+       int temp = 0;
+
+       /*
+        * The BTEMP events are not reliabe on AB8500 cut2.0
+        * and prior versions
+        */
+       if (is_ab8500_2p0_or_earlier(di->parent)) {
+               temp = di->bat_temp * 10;
+       } else {
+               if (di->events.btemp_low) {
+                       if (temp > di->btemp_ranges.btemp_low_limit)
+                               temp = di->btemp_ranges.btemp_low_limit;
+                       else
+                               temp = di->bat_temp * 10;
+               } else if (di->events.btemp_high) {
+                       if (temp < di->btemp_ranges.btemp_high_limit)
+                               temp = di->btemp_ranges.btemp_high_limit;
+                       else
+                               temp = di->bat_temp * 10;
+               } else if (di->events.btemp_lowmed) {
+                       if (temp > di->btemp_ranges.btemp_med_limit)
+                               temp = di->btemp_ranges.btemp_med_limit;
+                       else
+                               temp = di->bat_temp * 10;
+               } else if (di->events.btemp_medhigh) {
+                       if (temp < di->btemp_ranges.btemp_med_limit)
+                               temp = di->btemp_ranges.btemp_med_limit;
+                       else
+                               temp = di->bat_temp * 10;
+               } else
+                       temp = di->bat_temp * 10;
+       }
+       return temp;
+}
+
+/**
+ * ab8500_btemp_get_batctrl_temp() - get the temperature
+ * @btemp:      pointer to the btemp structure
+ *
+ * Returns the batctrl temperature in millidegrees
+ */
+int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp)
+{
+       return btemp->bat_temp * 1000;
+}
+
+/**
+ * ab8500_btemp_get_property() - get the btemp properties
+ * @psy:        pointer to the power_supply structure
+ * @psp:        pointer to the power_supply_property structure
+ * @val:        pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the btemp
+ * properties by reading the sysfs files.
+ * online:     presence of the battery
+ * present:    presence of the battery
+ * technology: battery technology
+ * temp:       battery temperature
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_btemp_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct ab8500_btemp *di;
+
+       di = to_ab8500_btemp_device_info(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+       case POWER_SUPPLY_PROP_ONLINE:
+               if (di->events.batt_rem)
+                       val->intval = 0;
+               else
+                       val->intval = 1;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = di->bat->bat_type[di->bat->batt_id].name;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = ab8500_btemp_get_temp(di);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
+{
+       struct power_supply *psy;
+       struct power_supply *ext;
+       struct ab8500_btemp *di;
+       union power_supply_propval ret;
+       int i, j;
+       bool psy_found = false;
+
+       psy = (struct power_supply *)data;
+       ext = dev_get_drvdata(dev);
+       di = to_ab8500_btemp_device_info(psy);
+
+       /*
+        * For all psy where the name of your driver
+        * appears in any supplied_to
+        */
+       for (i = 0; i < ext->num_supplicants; i++) {
+               if (!strcmp(ext->supplied_to[i], psy->name))
+                       psy_found = true;
+       }
+
+       if (!psy_found)
+               return 0;
+
+       /* Go through all properties for the psy */
+       for (j = 0; j < ext->num_properties; j++) {
+               enum power_supply_property prop;
+               prop = ext->properties[j];
+
+               if (ext->get_property(ext, prop, &ret))
+                       continue;
+
+               switch (prop) {
+               case POWER_SUPPLY_PROP_PRESENT:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               /* AC disconnected */
+                               if (!ret.intval && di->events.ac_conn) {
+                                       di->events.ac_conn = false;
+                               }
+                               /* AC connected */
+                               else if (ret.intval && !di->events.ac_conn) {
+                                       di->events.ac_conn = true;
+                                       if (!di->events.usb_conn)
+                                               ab8500_btemp_periodic(di, true);
+                               }
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               /* USB disconnected */
+                               if (!ret.intval && di->events.usb_conn) {
+                                       di->events.usb_conn = false;
+                               }
+                               /* USB connected */
+                               else if (ret.intval && !di->events.usb_conn) {
+                                       di->events.usb_conn = true;
+                                       if (!di->events.ac_conn)
+                                               ab8500_btemp_periodic(di, true);
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+/**
+ * ab8500_btemp_external_power_changed() - callback for power supply changes
+ * @psy:       pointer to the structure power_supply
+ *
+ * This function is pointing to the function pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in the external power
+ * supply to the btemp.
+ */
+static void ab8500_btemp_external_power_changed(struct power_supply *psy)
+{
+       struct ab8500_btemp *di = to_ab8500_btemp_device_info(psy);
+
+       class_for_each_device(power_supply_class, NULL,
+               &di->btemp_psy, ab8500_btemp_get_ext_psy_data);
+}
+
+/* ab8500 btemp driver interrupts and their respective isr */
+static struct ab8500_btemp_interrupts ab8500_btemp_irq[] = {
+       {"BAT_CTRL_INDB", ab8500_btemp_batctrlindb_handler},
+       {"BTEMP_LOW", ab8500_btemp_templow_handler},
+       {"BTEMP_HIGH", ab8500_btemp_temphigh_handler},
+       {"BTEMP_LOW_MEDIUM", ab8500_btemp_lowmed_handler},
+       {"BTEMP_MEDIUM_HIGH", ab8500_btemp_medhigh_handler},
+};
+
+#if defined(CONFIG_PM)
+static int ab8500_btemp_resume(struct platform_device *pdev)
+{
+       struct ab8500_btemp *di = platform_get_drvdata(pdev);
+
+       ab8500_btemp_periodic(di, true);
+
+       return 0;
+}
+
+static int ab8500_btemp_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct ab8500_btemp *di = platform_get_drvdata(pdev);
+
+       ab8500_btemp_periodic(di, false);
+
+       return 0;
+}
+#else
+#define ab8500_btemp_suspend      NULL
+#define ab8500_btemp_resume       NULL
+#endif
+
+static int __devexit ab8500_btemp_remove(struct platform_device *pdev)
+{
+       struct ab8500_btemp *di = platform_get_drvdata(pdev);
+       int i, irq;
+
+       /* Disable interrupts */
+       for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+               free_irq(irq, di);
+       }
+
+       /* Delete the work queue */
+       destroy_workqueue(di->btemp_wq);
+
+       flush_scheduled_work();
+       power_supply_unregister(&di->btemp_psy);
+       platform_set_drvdata(pdev, NULL);
+       kfree(di);
+
+       return 0;
+}
+
+static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
+{
+       int irq, i, ret = 0;
+       u8 val;
+       struct abx500_bm_plat_data *plat_data;
+
+       struct ab8500_btemp *di =
+               kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL);
+       if (!di)
+               return -ENOMEM;
+
+       /* get parent data */
+       di->dev = &pdev->dev;
+       di->parent = dev_get_drvdata(pdev->dev.parent);
+       di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+       /* get btemp specific platform data */
+       plat_data = pdev->dev.platform_data;
+       di->pdata = plat_data->btemp;
+       if (!di->pdata) {
+               dev_err(di->dev, "no btemp platform data supplied\n");
+               ret = -EINVAL;
+               goto free_device_info;
+       }
+
+       /* get battery specific platform data */
+       di->bat = plat_data->battery;
+       if (!di->bat) {
+               dev_err(di->dev, "no battery platform data supplied\n");
+               ret = -EINVAL;
+               goto free_device_info;
+       }
+
+       /* BTEMP supply */
+       di->btemp_psy.name = "ab8500_btemp";
+       di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+       di->btemp_psy.properties = ab8500_btemp_props;
+       di->btemp_psy.num_properties = ARRAY_SIZE(ab8500_btemp_props);
+       di->btemp_psy.get_property = ab8500_btemp_get_property;
+       di->btemp_psy.supplied_to = di->pdata->supplied_to;
+       di->btemp_psy.num_supplicants = di->pdata->num_supplicants;
+       di->btemp_psy.external_power_changed =
+               ab8500_btemp_external_power_changed;
+
+
+       /* Create a work queue for the btemp */
+       di->btemp_wq =
+               create_singlethread_workqueue("ab8500_btemp_wq");
+       if (di->btemp_wq == NULL) {
+               dev_err(di->dev, "failed to create work queue\n");
+               goto free_device_info;
+       }
+
+       /* Init work for measuring temperature periodically */
+       INIT_DELAYED_WORK_DEFERRABLE(&di->btemp_periodic_work,
+               ab8500_btemp_periodic_work);
+
+       /* Identify the battery */
+       if (ab8500_btemp_id(di) < 0)
+               dev_warn(di->dev, "failed to identify the battery\n");
+
+       /* Set BTEMP thermal limits. Low and Med are fixed */
+       di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
+       di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_BTEMP_HIGH_TH, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               goto free_btemp_wq;
+       }
+       switch (val) {
+       case BTEMP_HIGH_TH_57_0:
+       case BTEMP_HIGH_TH_57_1:
+               di->btemp_ranges.btemp_high_limit =
+                       BTEMP_THERMAL_HIGH_LIMIT_57;
+               break;
+       case BTEMP_HIGH_TH_52:
+               di->btemp_ranges.btemp_high_limit =
+                       BTEMP_THERMAL_HIGH_LIMIT_52;
+               break;
+       case BTEMP_HIGH_TH_62:
+               di->btemp_ranges.btemp_high_limit =
+                       BTEMP_THERMAL_HIGH_LIMIT_62;
+               break;
+       }
+
+       /* Register BTEMP power supply class */
+       ret = power_supply_register(di->dev, &di->btemp_psy);
+       if (ret) {
+               dev_err(di->dev, "failed to register BTEMP psy\n");
+               goto free_btemp_wq;
+       }
+
+       /* Register interrupts */
+       for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+               ret = request_threaded_irq(irq, NULL, ab8500_btemp_irq[i].isr,
+                       IRQF_SHARED | IRQF_NO_SUSPEND,
+                       ab8500_btemp_irq[i].name, di);
+
+               if (ret) {
+                       dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+                               , ab8500_btemp_irq[i].name, irq, ret);
+                       goto free_irq;
+               }
+               dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+                       ab8500_btemp_irq[i].name, irq, ret);
+       }
+
+       platform_set_drvdata(pdev, di);
+
+       /* Kick off periodic temperature measurements */
+       ab8500_btemp_periodic(di, true);
+       list_add_tail(&di->node, &ab8500_btemp_list);
+
+       return ret;
+
+free_irq:
+       power_supply_unregister(&di->btemp_psy);
+
+       /* We also have to free all successfully registered irqs */
+       for (i = i - 1; i >= 0; i--) {
+               irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+               free_irq(irq, di);
+       }
+free_btemp_wq:
+       destroy_workqueue(di->btemp_wq);
+free_device_info:
+       kfree(di);
+
+       return ret;
+}
+
+static struct platform_driver ab8500_btemp_driver = {
+       .probe = ab8500_btemp_probe,
+       .remove = __devexit_p(ab8500_btemp_remove),
+       .suspend = ab8500_btemp_suspend,
+       .resume = ab8500_btemp_resume,
+       .driver = {
+               .name = "ab8500-btemp",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init ab8500_btemp_init(void)
+{
+       return platform_driver_register(&ab8500_btemp_driver);
+}
+
+static void __exit ab8500_btemp_exit(void)
+{
+       platform_driver_unregister(&ab8500_btemp_driver);
+}
+
+subsys_initcall_sync(ab8500_btemp_init);
+module_exit(ab8500_btemp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
+MODULE_ALIAS("platform:ab8500-btemp");
+MODULE_DESCRIPTION("AB8500 battery temperature driver");
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
new file mode 100644 (file)
index 0000000..e2b4acc
--- /dev/null
@@ -0,0 +1,2789 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Charger driver for AB8500
+ *
+ * License Terms: GNU General Public License v2
+ * Author:
+ *     Johan Palsson <johan.palsson@stericsson.com>
+ *     Karl Komierowski <karl.komierowski@stericsson.com>
+ *     Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/usb/otg.h>
+
+/* Charger constants */
+#define NO_PW_CONN                     0
+#define AC_PW_CONN                     1
+#define USB_PW_CONN                    2
+
+#define MAIN_WDOG_ENA                  0x01
+#define MAIN_WDOG_KICK                 0x02
+#define MAIN_WDOG_DIS                  0x00
+#define CHARG_WD_KICK                  0x01
+#define MAIN_CH_ENA                    0x01
+#define MAIN_CH_NO_OVERSHOOT_ENA_N     0x02
+#define USB_CH_ENA                     0x01
+#define USB_CHG_NO_OVERSHOOT_ENA_N     0x02
+#define MAIN_CH_DET                    0x01
+#define MAIN_CH_CV_ON                  0x04
+#define USB_CH_CV_ON                   0x08
+#define VBUS_DET_DBNC100               0x02
+#define VBUS_DET_DBNC1                 0x01
+#define OTP_ENABLE_WD                  0x01
+
+#define MAIN_CH_INPUT_CURR_SHIFT       4
+#define VBUS_IN_CURR_LIM_SHIFT         4
+
+#define LED_INDICATOR_PWM_ENA          0x01
+#define LED_INDICATOR_PWM_DIS          0x00
+#define LED_IND_CUR_5MA                        0x04
+#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
+
+/* HW failure constants */
+#define MAIN_CH_TH_PROT                        0x02
+#define VBUS_CH_NOK                    0x08
+#define USB_CH_TH_PROT                 0x02
+#define VBUS_OVV_TH                    0x01
+#define MAIN_CH_NOK                    0x01
+#define VBUS_DET                       0x80
+
+/* UsbLineStatus register bit masks */
+#define AB8500_USB_LINK_STATUS         0x78
+#define AB8500_STD_HOST_SUSP           0x18
+
+/* Watchdog timeout constant */
+#define WD_TIMER                       0x30 /* 4min */
+#define WD_KICK_INTERVAL               (60 * HZ)
+
+/* Lowest charger voltage is 3.39V -> 0x4E */
+#define LOW_VOLT_REG                   0x4E
+
+/* UsbLineStatus register - usb types */
+enum ab8500_charger_link_status {
+       USB_STAT_NOT_CONFIGURED,
+       USB_STAT_STD_HOST_NC,
+       USB_STAT_STD_HOST_C_NS,
+       USB_STAT_STD_HOST_C_S,
+       USB_STAT_HOST_CHG_NM,
+       USB_STAT_HOST_CHG_HS,
+       USB_STAT_HOST_CHG_HS_CHIRP,
+       USB_STAT_DEDICATED_CHG,
+       USB_STAT_ACA_RID_A,
+       USB_STAT_ACA_RID_B,
+       USB_STAT_ACA_RID_C_NM,
+       USB_STAT_ACA_RID_C_HS,
+       USB_STAT_ACA_RID_C_HS_CHIRP,
+       USB_STAT_HM_IDGND,
+       USB_STAT_RESERVED,
+       USB_STAT_NOT_VALID_LINK,
+};
+
+enum ab8500_usb_state {
+       AB8500_BM_USB_STATE_RESET_HS,   /* HighSpeed Reset */
+       AB8500_BM_USB_STATE_RESET_FS,   /* FullSpeed/LowSpeed Reset */
+       AB8500_BM_USB_STATE_CONFIGURED,
+       AB8500_BM_USB_STATE_SUSPEND,
+       AB8500_BM_USB_STATE_RESUME,
+       AB8500_BM_USB_STATE_MAX,
+};
+
+/* VBUS input current limits supported in AB8500 in mA */
+#define USB_CH_IP_CUR_LVL_0P05         50
+#define USB_CH_IP_CUR_LVL_0P09         98
+#define USB_CH_IP_CUR_LVL_0P19         193
+#define USB_CH_IP_CUR_LVL_0P29         290
+#define USB_CH_IP_CUR_LVL_0P38         380
+#define USB_CH_IP_CUR_LVL_0P45         450
+#define USB_CH_IP_CUR_LVL_0P5          500
+#define USB_CH_IP_CUR_LVL_0P6          600
+#define USB_CH_IP_CUR_LVL_0P7          700
+#define USB_CH_IP_CUR_LVL_0P8          800
+#define USB_CH_IP_CUR_LVL_0P9          900
+#define USB_CH_IP_CUR_LVL_1P0          1000
+#define USB_CH_IP_CUR_LVL_1P1          1100
+#define USB_CH_IP_CUR_LVL_1P3          1300
+#define USB_CH_IP_CUR_LVL_1P4          1400
+#define USB_CH_IP_CUR_LVL_1P5          1500
+
+#define VBAT_TRESH_IP_CUR_RED          3800
+
+#define to_ab8500_charger_usb_device_info(x) container_of((x), \
+       struct ab8500_charger, usb_chg)
+#define to_ab8500_charger_ac_device_info(x) container_of((x), \
+       struct ab8500_charger, ac_chg)
+
+/**
+ * struct ab8500_charger_interrupts - ab8500 interupts
+ * @name:      name of the interrupt
+ * @isr                function pointer to the isr
+ */
+struct ab8500_charger_interrupts {
+       char *name;
+       irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab8500_charger_info {
+       int charger_connected;
+       int charger_online;
+       int charger_voltage;
+       int cv_active;
+       bool wd_expired;
+};
+
+struct ab8500_charger_event_flags {
+       bool mainextchnotok;
+       bool main_thermal_prot;
+       bool usb_thermal_prot;
+       bool vbus_ovv;
+       bool usbchargernotok;
+       bool chgwdexp;
+       bool vbus_collapse;
+};
+
+struct ab8500_charger_usb_state {
+       bool usb_changed;
+       int usb_current;
+       enum ab8500_usb_state state;
+       spinlock_t usb_lock;
+};
+
+/**
+ * struct ab8500_charger - ab8500 Charger device information
+ * @dev:               Pointer to the structure device
+ * @max_usb_in_curr:   Max USB charger input current
+ * @vbus_detected:     VBUS detected
+ * @vbus_detected_start:
+ *                     VBUS detected during startup
+ * @ac_conn:           This will be true when the AC charger has been plugged
+ * @vddadc_en_ac:      Indicate if VDD ADC supply is enabled because AC
+ *                     charger is enabled
+ * @vddadc_en_usb:     Indicate if VDD ADC supply is enabled because USB
+ *                     charger is enabled
+ * @vbat               Battery voltage
+ * @old_vbat           Previously measured battery voltage
+ * @autopower          Indicate if we should have automatic pwron after pwrloss
+ * @parent:            Pointer to the struct ab8500
+ * @gpadc:             Pointer to the struct gpadc
+ * @pdata:             Pointer to the abx500_charger platform data
+ * @bat:               Pointer to the abx500_bm platform data
+ * @flags:             Structure for information about events triggered
+ * @usb_state:         Structure for usb stack information
+ * @ac_chg:            AC charger power supply
+ * @usb_chg:           USB charger power supply
+ * @ac:                        Structure that holds the AC charger properties
+ * @usb:               Structure that holds the USB charger properties
+ * @regu:              Pointer to the struct regulator
+ * @charger_wq:                Work queue for the IRQs and checking HW state
+ * @check_vbat_work    Work for checking vbat threshold to adjust vbus current
+ * @check_hw_failure_work:     Work for checking HW state
+ * @check_usbchgnotok_work:    Work for checking USB charger not ok status
+ * @kick_wd_work:              Work for kicking the charger watchdog in case
+ *                             of ABB rev 1.* due to the watchog logic bug
+ * @ac_work:                   Work for checking AC charger connection
+ * @detect_usb_type_work:      Work for detecting the USB type connected
+ * @usb_link_status_work:      Work for checking the new USB link status
+ * @usb_state_changed_work:    Work for checking USB state
+ * @check_main_thermal_prot_work:
+ *                             Work for checking Main thermal status
+ * @check_usb_thermal_prot_work:
+ *                             Work for checking USB thermal status
+ */
+struct ab8500_charger {
+       struct device *dev;
+       int max_usb_in_curr;
+       bool vbus_detected;
+       bool vbus_detected_start;
+       bool ac_conn;
+       bool vddadc_en_ac;
+       bool vddadc_en_usb;
+       int vbat;
+       int old_vbat;
+       bool autopower;
+       struct ab8500 *parent;
+       struct ab8500_gpadc *gpadc;
+       struct abx500_charger_platform_data *pdata;
+       struct abx500_bm_data *bat;
+       struct ab8500_charger_event_flags flags;
+       struct ab8500_charger_usb_state usb_state;
+       struct ux500_charger ac_chg;
+       struct ux500_charger usb_chg;
+       struct ab8500_charger_info ac;
+       struct ab8500_charger_info usb;
+       struct regulator *regu;
+       struct workqueue_struct *charger_wq;
+       struct delayed_work check_vbat_work;
+       struct delayed_work check_hw_failure_work;
+       struct delayed_work check_usbchgnotok_work;
+       struct delayed_work kick_wd_work;
+       struct work_struct ac_work;
+       struct work_struct detect_usb_type_work;
+       struct work_struct usb_link_status_work;
+       struct work_struct usb_state_changed_work;
+       struct work_struct check_main_thermal_prot_work;
+       struct work_struct check_usb_thermal_prot_work;
+       struct usb_phy *usb_phy;
+       struct notifier_block nb;
+};
+
+/* AC properties */
+static enum power_supply_property ab8500_charger_ac_props[] = {
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+/* USB properties */
+static enum power_supply_property ab8500_charger_usb_props[] = {
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+/**
+ * ab8500_power_loss_handling - set how we handle powerloss.
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Magic nummbers are from STE HW department.
+ */
+static void ab8500_power_loss_handling(struct ab8500_charger *di)
+{
+       u8 reg;
+       int ret;
+
+       dev_dbg(di->dev, "Autopower : %d\n", di->autopower);
+
+       /* read the autopower register */
+       ret = abx500_get_register_interruptible(di->dev, 0x15, 0x00, &reg);
+       if (ret) {
+               dev_err(di->dev, "%d write failed\n", __LINE__);
+               return;
+       }
+
+       /* enable the OPT emulation registers */
+       ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x2);
+       if (ret) {
+               dev_err(di->dev, "%d write failed\n", __LINE__);
+               return;
+       }
+
+       if (di->autopower)
+               reg |= 0x8;
+       else
+               reg &= ~0x8;
+
+       /* write back the changed value to autopower reg */
+       ret = abx500_set_register_interruptible(di->dev, 0x15, 0x00, reg);
+       if (ret) {
+               dev_err(di->dev, "%d write failed\n", __LINE__);
+               return;
+       }
+
+       /* disable the set OTP registers again */
+       ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x0);
+       if (ret) {
+               dev_err(di->dev, "%d write failed\n", __LINE__);
+               return;
+       }
+}
+
+/**
+ * ab8500_power_supply_changed - a wrapper with local extentions for
+ * power_supply_changed
+ * @di:          pointer to the ab8500_charger structure
+ * @psy:  pointer to power_supply_that have changed.
+ *
+ */
+static void ab8500_power_supply_changed(struct ab8500_charger *di,
+                                       struct power_supply *psy)
+{
+       if (di->pdata->autopower_cfg) {
+               if (!di->usb.charger_connected &&
+                   !di->ac.charger_connected &&
+                   di->autopower) {
+                       di->autopower = false;
+                       ab8500_power_loss_handling(di);
+               } else if (!di->autopower &&
+                          (di->ac.charger_connected ||
+                           di->usb.charger_connected)) {
+                       di->autopower = true;
+                       ab8500_power_loss_handling(di);
+               }
+       }
+       power_supply_changed(psy);
+}
+
+static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
+       bool connected)
+{
+       if (connected != di->usb.charger_connected) {
+               dev_dbg(di->dev, "USB connected:%i\n", connected);
+               di->usb.charger_connected = connected;
+               sysfs_notify(&di->usb_chg.psy.dev->kobj, NULL, "present");
+       }
+}
+
+/**
+ * ab8500_charger_get_ac_voltage() - get ac charger voltage
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Returns ac charger voltage (on success)
+ */
+static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di)
+{
+       int vch;
+
+       /* Only measure voltage if the charger is connected */
+       if (di->ac.charger_connected) {
+               vch = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_V);
+               if (vch < 0)
+                       dev_err(di->dev, "%s gpadc conv failed,\n", __func__);
+       } else {
+               vch = 0;
+       }
+       return vch;
+}
+
+/**
+ * ab8500_charger_ac_cv() - check if the main charger is in CV mode
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Returns ac charger CV mode (on success) else error code
+ */
+static int ab8500_charger_ac_cv(struct ab8500_charger *di)
+{
+       u8 val;
+       int ret = 0;
+
+       /* Only check CV mode if the charger is online */
+       if (di->ac.charger_online) {
+               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_STATUS1_REG, &val);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return 0;
+               }
+
+               if (val & MAIN_CH_CV_ON)
+                       ret = 1;
+               else
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_get_vbus_voltage() - get vbus voltage
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * This function returns the vbus voltage.
+ * Returns vbus voltage (on success)
+ */
+static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di)
+{
+       int vch;
+
+       /* Only measure voltage if the charger is connected */
+       if (di->usb.charger_connected) {
+               vch = ab8500_gpadc_convert(di->gpadc, VBUS_V);
+               if (vch < 0)
+                       dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+       } else {
+               vch = 0;
+       }
+       return vch;
+}
+
+/**
+ * ab8500_charger_get_usb_current() - get usb charger current
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * This function returns the usb charger current.
+ * Returns usb current (on success) and error code on failure
+ */
+static int ab8500_charger_get_usb_current(struct ab8500_charger *di)
+{
+       int ich;
+
+       /* Only measure current if the charger is online */
+       if (di->usb.charger_online) {
+               ich = ab8500_gpadc_convert(di->gpadc, USB_CHARGER_C);
+               if (ich < 0)
+                       dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+       } else {
+               ich = 0;
+       }
+       return ich;
+}
+
+/**
+ * ab8500_charger_get_ac_current() - get ac charger current
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * This function returns the ac charger current.
+ * Returns ac current (on success) and error code on failure.
+ */
+static int ab8500_charger_get_ac_current(struct ab8500_charger *di)
+{
+       int ich;
+
+       /* Only measure current if the charger is online */
+       if (di->ac.charger_online) {
+               ich = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_C);
+               if (ich < 0)
+                       dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+       } else {
+               ich = 0;
+       }
+       return ich;
+}
+
+/**
+ * ab8500_charger_usb_cv() - check if the usb charger is in CV mode
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Returns ac charger CV mode (on success) else error code
+ */
+static int ab8500_charger_usb_cv(struct ab8500_charger *di)
+{
+       int ret;
+       u8 val;
+
+       /* Only check CV mode if the charger is online */
+       if (di->usb.charger_online) {
+               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_USBCH_STAT1_REG, &val);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return 0;
+               }
+
+               if (val & USB_CH_CV_ON)
+                       ret = 1;
+               else
+                       ret = 0;
+       } else {
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_detect_chargers() - Detect the connected chargers
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Returns the type of charger connected.
+ * For USB it will not mean we can actually charge from it
+ * but that there is a USB cable connected that we have to
+ * identify. This is used during startup when we don't get
+ * interrupts of the charger detection
+ *
+ * Returns an integer value, that means,
+ * NO_PW_CONN  no power supply is connected
+ * AC_PW_CONN  if the AC power supply is connected
+ * USB_PW_CONN  if the USB power supply is connected
+ * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
+ */
+static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
+{
+       int result = NO_PW_CONN;
+       int ret;
+       u8 val;
+
+       /* Check for AC charger */
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CH_STATUS1_REG, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return ret;
+       }
+
+       if (val & MAIN_CH_DET)
+               result = AC_PW_CONN;
+
+       /* Check for USB charger */
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CH_USBCH_STAT1_REG, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return ret;
+       }
+
+       if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
+               result |= USB_PW_CONN;
+
+       return result;
+}
+
+/**
+ * ab8500_charger_max_usb_curr() - get the max curr for the USB type
+ * @di:                        pointer to the ab8500_charger structure
+ * @link_status:       the identified USB type
+ *
+ * Get the maximum current that is allowed to be drawn from the host
+ * based on the USB type.
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
+       enum ab8500_charger_link_status link_status)
+{
+       int ret = 0;
+
+       switch (link_status) {
+       case USB_STAT_STD_HOST_NC:
+       case USB_STAT_STD_HOST_C_NS:
+       case USB_STAT_STD_HOST_C_S:
+               dev_dbg(di->dev, "USB Type - Standard host is "
+                       "detected through USB driver\n");
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+               break;
+       case USB_STAT_HOST_CHG_HS_CHIRP:
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+               break;
+       case USB_STAT_HOST_CHG_HS:
+       case USB_STAT_ACA_RID_C_HS:
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
+               break;
+       case USB_STAT_ACA_RID_A:
+               /*
+                * Dedicated charger level minus maximum current accessory
+                * can consume (300mA). Closest level is 1100mA
+                */
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
+               break;
+       case USB_STAT_ACA_RID_B:
+               /*
+                * Dedicated charger level minus 120mA (20mA for ACA and
+                * 100mA for potential accessory). Closest level is 1300mA
+                */
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
+               break;
+       case USB_STAT_DEDICATED_CHG:
+       case USB_STAT_HOST_CHG_NM:
+       case USB_STAT_ACA_RID_C_HS_CHIRP:
+       case USB_STAT_ACA_RID_C_NM:
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+               break;
+       case USB_STAT_RESERVED:
+               /*
+                * This state is used to indicate that VBUS has dropped below
+                * the detection level 4 times in a row. This is due to the
+                * charger output current is set to high making the charger
+                * voltage collapse. This have to be propagated through to
+                * chargalg. This is done using the property
+                * POWER_SUPPLY_PROP_CURRENT_AVG = 1
+                */
+               di->flags.vbus_collapse = true;
+               dev_dbg(di->dev, "USB Type - USB_STAT_RESERVED "
+                       "VBUS has collapsed\n");
+               ret = -1;
+               break;
+       case USB_STAT_HM_IDGND:
+       case USB_STAT_NOT_CONFIGURED:
+       case USB_STAT_NOT_VALID_LINK:
+               dev_err(di->dev, "USB Type - Charging not allowed\n");
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+               ret = -ENXIO;
+               break;
+       default:
+               dev_err(di->dev, "USB Type - Unknown\n");
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+               ret = -ENXIO;
+               break;
+       };
+
+       dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
+               link_status, di->max_usb_in_curr);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_read_usb_type() - read the type of usb connected
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Detect the type of the plugged USB
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
+{
+       int ret;
+       u8 val;
+
+       ret = abx500_get_register_interruptible(di->dev,
+               AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return ret;
+       }
+       ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+               AB8500_USB_LINE_STAT_REG, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return ret;
+       }
+
+       /* get the USB type */
+       val = (val & AB8500_USB_LINK_STATUS) >> 3;
+       ret = ab8500_charger_max_usb_curr(di,
+               (enum ab8500_charger_link_status) val);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_detect_usb_type() - get the type of usb connected
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Detect the type of the plugged USB
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
+{
+       int i, ret;
+       u8 val;
+
+       /*
+        * On getting the VBUS rising edge detect interrupt there
+        * is a 250ms delay after which the register UsbLineStatus
+        * is filled with valid data.
+        */
+       for (i = 0; i < 10; i++) {
+               msleep(250);
+               ret = abx500_get_register_interruptible(di->dev,
+                       AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
+                       &val);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return ret;
+               }
+               ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+                       AB8500_USB_LINE_STAT_REG, &val);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return ret;
+               }
+               /*
+                * Until the IT source register is read the UsbLineStatus
+                * register is not updated, hence doing the same
+                * Revisit this:
+                */
+
+               /* get the USB type */
+               val = (val & AB8500_USB_LINK_STATUS) >> 3;
+               if (val)
+                       break;
+       }
+       ret = ab8500_charger_max_usb_curr(di,
+               (enum ab8500_charger_link_status) val);
+
+       return ret;
+}
+
+/*
+ * This array maps the raw hex value to charger voltage used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_voltage_map[] = {
+       3500 ,
+       3525 ,
+       3550 ,
+       3575 ,
+       3600 ,
+       3625 ,
+       3650 ,
+       3675 ,
+       3700 ,
+       3725 ,
+       3750 ,
+       3775 ,
+       3800 ,
+       3825 ,
+       3850 ,
+       3875 ,
+       3900 ,
+       3925 ,
+       3950 ,
+       3975 ,
+       4000 ,
+       4025 ,
+       4050 ,
+       4060 ,
+       4070 ,
+       4080 ,
+       4090 ,
+       4100 ,
+       4110 ,
+       4120 ,
+       4130 ,
+       4140 ,
+       4150 ,
+       4160 ,
+       4170 ,
+       4180 ,
+       4190 ,
+       4200 ,
+       4210 ,
+       4220 ,
+       4230 ,
+       4240 ,
+       4250 ,
+       4260 ,
+       4270 ,
+       4280 ,
+       4290 ,
+       4300 ,
+       4310 ,
+       4320 ,
+       4330 ,
+       4340 ,
+       4350 ,
+       4360 ,
+       4370 ,
+       4380 ,
+       4390 ,
+       4400 ,
+       4410 ,
+       4420 ,
+       4430 ,
+       4440 ,
+       4450 ,
+       4460 ,
+       4470 ,
+       4480 ,
+       4490 ,
+       4500 ,
+       4510 ,
+       4520 ,
+       4530 ,
+       4540 ,
+       4550 ,
+       4560 ,
+       4570 ,
+       4580 ,
+       4590 ,
+       4600 ,
+};
+
+/*
+ * This array maps the raw hex value to charger current used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_current_map[] = {
+       100 ,
+       200 ,
+       300 ,
+       400 ,
+       500 ,
+       600 ,
+       700 ,
+       800 ,
+       900 ,
+       1000 ,
+       1100 ,
+       1200 ,
+       1300 ,
+       1400 ,
+       1500 ,
+};
+
+/*
+ * This array maps the raw hex value to VBUS input current used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_vbus_in_curr_map[] = {
+       USB_CH_IP_CUR_LVL_0P05,
+       USB_CH_IP_CUR_LVL_0P09,
+       USB_CH_IP_CUR_LVL_0P19,
+       USB_CH_IP_CUR_LVL_0P29,
+       USB_CH_IP_CUR_LVL_0P38,
+       USB_CH_IP_CUR_LVL_0P45,
+       USB_CH_IP_CUR_LVL_0P5,
+       USB_CH_IP_CUR_LVL_0P6,
+       USB_CH_IP_CUR_LVL_0P7,
+       USB_CH_IP_CUR_LVL_0P8,
+       USB_CH_IP_CUR_LVL_0P9,
+       USB_CH_IP_CUR_LVL_1P0,
+       USB_CH_IP_CUR_LVL_1P1,
+       USB_CH_IP_CUR_LVL_1P3,
+       USB_CH_IP_CUR_LVL_1P4,
+       USB_CH_IP_CUR_LVL_1P5,
+};
+
+static int ab8500_voltage_to_regval(int voltage)
+{
+       int i;
+
+       /* Special case for voltage below 3.5V */
+       if (voltage < ab8500_charger_voltage_map[0])
+               return LOW_VOLT_REG;
+
+       for (i = 1; i < ARRAY_SIZE(ab8500_charger_voltage_map); i++) {
+               if (voltage < ab8500_charger_voltage_map[i])
+                       return i - 1;
+       }
+
+       /* If not last element, return error */
+       i = ARRAY_SIZE(ab8500_charger_voltage_map) - 1;
+       if (voltage == ab8500_charger_voltage_map[i])
+               return i;
+       else
+               return -1;
+}
+
+static int ab8500_current_to_regval(int curr)
+{
+       int i;
+
+       if (curr < ab8500_charger_current_map[0])
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(ab8500_charger_current_map); i++) {
+               if (curr < ab8500_charger_current_map[i])
+                       return i - 1;
+       }
+
+       /* If not last element, return error */
+       i = ARRAY_SIZE(ab8500_charger_current_map) - 1;
+       if (curr == ab8500_charger_current_map[i])
+               return i;
+       else
+               return -1;
+}
+
+static int ab8500_vbus_in_curr_to_regval(int curr)
+{
+       int i;
+
+       if (curr < ab8500_charger_vbus_in_curr_map[0])
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(ab8500_charger_vbus_in_curr_map); i++) {
+               if (curr < ab8500_charger_vbus_in_curr_map[i])
+                       return i - 1;
+       }
+
+       /* If not last element, return error */
+       i = ARRAY_SIZE(ab8500_charger_vbus_in_curr_map) - 1;
+       if (curr == ab8500_charger_vbus_in_curr_map[i])
+               return i;
+       else
+               return -1;
+}
+
+/**
+ * ab8500_charger_get_usb_cur() - get usb current
+ * @di:                pointer to the ab8500_charger structre
+ *
+ * The usb stack provides the maximum current that can be drawn from
+ * the standard usb host. This will be in mA.
+ * This function converts current in mA to a value that can be written
+ * to the register. Returns -1 if charging is not allowed
+ */
+static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
+{
+       switch (di->usb_state.usb_current) {
+       case 100:
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+               break;
+       case 200:
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P19;
+               break;
+       case 300:
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P29;
+               break;
+       case 400:
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P38;
+               break;
+       case 500:
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+               break;
+       default:
+               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+               return -1;
+               break;
+       };
+       return 0;
+}
+
+/**
+ * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit
+ * @di:                pointer to the ab8500_charger structure
+ * @ich_in:    charger input current limit
+ *
+ * Sets the current that can be drawn from the USB host
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
+               int ich_in)
+{
+       int ret;
+       int input_curr_index;
+       int min_value;
+
+       /* We should always use to lowest current limit */
+       min_value = min(di->bat->chg_params->usb_curr_max, ich_in);
+
+       switch (min_value) {
+       case 100:
+               if (di->vbat < VBAT_TRESH_IP_CUR_RED)
+                       min_value = USB_CH_IP_CUR_LVL_0P05;
+               break;
+       case 500:
+               if (di->vbat < VBAT_TRESH_IP_CUR_RED)
+                       min_value = USB_CH_IP_CUR_LVL_0P45;
+               break;
+       default:
+               break;
+       }
+
+       input_curr_index = ab8500_vbus_in_curr_to_regval(min_value);
+       if (input_curr_index < 0) {
+               dev_err(di->dev, "VBUS input current limit too high\n");
+               return -ENXIO;
+       }
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_USBCH_IPT_CRNTLVL_REG,
+               input_curr_index << VBUS_IN_CURR_LIM_SHIFT);
+       if (ret)
+               dev_err(di->dev, "%s write failed\n", __func__);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_led_en() - turn on/off chargign led
+ * @di:                pointer to the ab8500_charger structure
+ * @on:                flag to turn on/off the chargign led
+ *
+ * Power ON/OFF charging LED indication
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_led_en(struct ab8500_charger *di, int on)
+{
+       int ret;
+
+       if (on) {
+               /* Power ON charging LED indicator, set LED current to 5mA */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_LED_INDICATOR_PWM_CTRL,
+                       (LED_IND_CUR_5MA | LED_INDICATOR_PWM_ENA));
+               if (ret) {
+                       dev_err(di->dev, "Power ON LED failed\n");
+                       return ret;
+               }
+               /* LED indicator PWM duty cycle 252/256 */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_LED_INDICATOR_PWM_DUTY,
+                       LED_INDICATOR_PWM_DUTY_252_256);
+               if (ret) {
+                       dev_err(di->dev, "Set LED PWM duty cycle failed\n");
+                       return ret;
+               }
+       } else {
+               /* Power off charging LED indicator */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_LED_INDICATOR_PWM_CTRL,
+                       LED_INDICATOR_PWM_DIS);
+               if (ret) {
+                       dev_err(di->dev, "Power-off LED failed\n");
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_ac_en() - enable or disable ac charging
+ * @di:                pointer to the ab8500_charger structure
+ * @enable:    enable/disable flag
+ * @vset:      charging voltage
+ * @iset:      charging current
+ *
+ * Enable/Disable AC/Mains charging and turns on/off the charging led
+ * respectively.
+ **/
+static int ab8500_charger_ac_en(struct ux500_charger *charger,
+       int enable, int vset, int iset)
+{
+       int ret;
+       int volt_index;
+       int curr_index;
+       int input_curr_index;
+       u8 overshoot = 0;
+
+       struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
+
+       if (enable) {
+               /* Check if AC is connected */
+               if (!di->ac.charger_connected) {
+                       dev_err(di->dev, "AC charger not connected\n");
+                       return -ENXIO;
+               }
+
+               /* Enable AC charging */
+               dev_dbg(di->dev, "Enable AC: %dmV %dmA\n", vset, iset);
+
+               /*
+                * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
+                * will be triggered everytime we enable the VDD ADC supply.
+                * This will turn off charging for a short while.
+                * It can be avoided by having the supply on when
+                * there is a charger enabled. Normally the VDD ADC supply
+                * is enabled everytime a GPADC conversion is triggered. We will
+                * force it to be enabled from this driver to have
+                * the GPADC module independant of the AB8500 chargers
+                */
+               if (!di->vddadc_en_ac) {
+                       regulator_enable(di->regu);
+                       di->vddadc_en_ac = true;
+               }
+
+               /* Check if the requested voltage or current is valid */
+               volt_index = ab8500_voltage_to_regval(vset);
+               curr_index = ab8500_current_to_regval(iset);
+               input_curr_index = ab8500_current_to_regval(
+                       di->bat->chg_params->ac_curr_max);
+               if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) {
+                       dev_err(di->dev,
+                               "Charger voltage or current too high, "
+                               "charging not started\n");
+                       return -ENXIO;
+               }
+
+               /* ChVoltLevel: maximum battery charging voltage */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+               /* MainChInputCurr: current that can be drawn from the charger*/
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_MCH_IPT_CURLVL_REG,
+                       input_curr_index << MAIN_CH_INPUT_CURR_SHIFT);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+               /* ChOutputCurentLevel: protected output current */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+
+               /* Check if VBAT overshoot control should be enabled */
+               if (!di->bat->enable_overshoot)
+                       overshoot = MAIN_CH_NO_OVERSHOOT_ENA_N;
+
+               /* Enable Main Charger */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_MCH_CTRL1, MAIN_CH_ENA | overshoot);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+
+               /* Power on charging LED indication */
+               ret = ab8500_charger_led_en(di, true);
+               if (ret < 0)
+                       dev_err(di->dev, "failed to enable LED\n");
+
+               di->ac.charger_online = 1;
+       } else {
+               /* Disable AC charging */
+               if (is_ab8500_1p1_or_earlier(di->parent)) {
+                       /*
+                        * For ABB revision 1.0 and 1.1 there is a bug in the
+                        * watchdog logic. That means we have to continously
+                        * kick the charger watchdog even when no charger is
+                        * connected. This is only valid once the AC charger
+                        * has been enabled. This is a bug that is not handled
+                        * by the algorithm and the watchdog have to be kicked
+                        * by the charger driver when the AC charger
+                        * is disabled
+                        */
+                       if (di->ac_conn) {
+                               queue_delayed_work(di->charger_wq,
+                                       &di->kick_wd_work,
+                                       round_jiffies(WD_KICK_INTERVAL));
+                       }
+
+                       /*
+                        * We can't turn off charging completely
+                        * due to a bug in AB8500 cut1.
+                        * If we do, charging will not start again.
+                        * That is why we set the lowest voltage
+                        * and current possible
+                        */
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_CHARGER,
+                               AB8500_CH_VOLT_LVL_REG, CH_VOL_LVL_3P5);
+                       if (ret) {
+                               dev_err(di->dev,
+                                       "%s write failed\n", __func__);
+                               return ret;
+                       }
+
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_CHARGER,
+                               AB8500_CH_OPT_CRNTLVL_REG, CH_OP_CUR_LVL_0P1);
+                       if (ret) {
+                               dev_err(di->dev,
+                                       "%s write failed\n", __func__);
+                               return ret;
+                       }
+               } else {
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_CHARGER,
+                               AB8500_MCH_CTRL1, 0);
+                       if (ret) {
+                               dev_err(di->dev,
+                                       "%s write failed\n", __func__);
+                               return ret;
+                       }
+               }
+
+               ret = ab8500_charger_led_en(di, false);
+               if (ret < 0)
+                       dev_err(di->dev, "failed to disable LED\n");
+
+               di->ac.charger_online = 0;
+               di->ac.wd_expired = false;
+
+               /* Disable regulator if enabled */
+               if (di->vddadc_en_ac) {
+                       regulator_disable(di->regu);
+                       di->vddadc_en_ac = false;
+               }
+
+               dev_dbg(di->dev, "%s Disabled AC charging\n", __func__);
+       }
+       ab8500_power_supply_changed(di, &di->ac_chg.psy);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_usb_en() - enable usb charging
+ * @di:                pointer to the ab8500_charger structure
+ * @enable:    enable/disable flag
+ * @vset:      charging voltage
+ * @ich_out:   charger output current
+ *
+ * Enable/Disable USB charging and turns on/off the charging led respectively.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_usb_en(struct ux500_charger *charger,
+       int enable, int vset, int ich_out)
+{
+       int ret;
+       int volt_index;
+       int curr_index;
+       u8 overshoot = 0;
+
+       struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
+
+       if (enable) {
+               /* Check if USB is connected */
+               if (!di->usb.charger_connected) {
+                       dev_err(di->dev, "USB charger not connected\n");
+                       return -ENXIO;
+               }
+
+               /*
+                * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
+                * will be triggered everytime we enable the VDD ADC supply.
+                * This will turn off charging for a short while.
+                * It can be avoided by having the supply on when
+                * there is a charger enabled. Normally the VDD ADC supply
+                * is enabled everytime a GPADC conversion is triggered. We will
+                * force it to be enabled from this driver to have
+                * the GPADC module independant of the AB8500 chargers
+                */
+               if (!di->vddadc_en_usb) {
+                       regulator_enable(di->regu);
+                       di->vddadc_en_usb = true;
+               }
+
+               /* Enable USB charging */
+               dev_dbg(di->dev, "Enable USB: %dmV %dmA\n", vset, ich_out);
+
+               /* Check if the requested voltage or current is valid */
+               volt_index = ab8500_voltage_to_regval(vset);
+               curr_index = ab8500_current_to_regval(ich_out);
+               if (volt_index < 0 || curr_index < 0) {
+                       dev_err(di->dev,
+                               "Charger voltage or current too high, "
+                               "charging not started\n");
+                       return -ENXIO;
+               }
+
+               /* ChVoltLevel: max voltage upto which battery can be charged */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+               /* USBChInputCurr: current that can be drawn from the usb */
+               ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+               if (ret) {
+                       dev_err(di->dev, "setting USBChInputCurr failed\n");
+                       return ret;
+               }
+               /* ChOutputCurentLevel: protected output current */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+               /* Check if VBAT overshoot control should be enabled */
+               if (!di->bat->enable_overshoot)
+                       overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
+
+               /* Enable USB Charger */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+
+               /* If success power on charging LED indication */
+               ret = ab8500_charger_led_en(di, true);
+               if (ret < 0)
+                       dev_err(di->dev, "failed to enable LED\n");
+
+               queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
+
+               di->usb.charger_online = 1;
+       } else {
+               /* Disable USB charging */
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_CHARGER,
+                       AB8500_USBCH_CTRL1_REG, 0);
+               if (ret) {
+                       dev_err(di->dev,
+                               "%s write failed\n", __func__);
+                       return ret;
+               }
+
+               ret = ab8500_charger_led_en(di, false);
+               if (ret < 0)
+                       dev_err(di->dev, "failed to disable LED\n");
+
+               di->usb.charger_online = 0;
+               di->usb.wd_expired = false;
+
+               /* Disable regulator if enabled */
+               if (di->vddadc_en_usb) {
+                       regulator_disable(di->regu);
+                       di->vddadc_en_usb = false;
+               }
+
+               dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
+
+               /* Cancel any pending Vbat check work */
+               if (delayed_work_pending(&di->check_vbat_work))
+                       cancel_delayed_work(&di->check_vbat_work);
+
+       }
+       ab8500_power_supply_changed(di, &di->usb_chg.psy);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_watchdog_kick() - kick charger watchdog
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Kick charger watchdog
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_watchdog_kick(struct ux500_charger *charger)
+{
+       int ret;
+       struct ab8500_charger *di;
+
+       if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+               di = to_ab8500_charger_ac_device_info(charger);
+       else if (charger->psy.type == POWER_SUPPLY_TYPE_USB)
+               di = to_ab8500_charger_usb_device_info(charger);
+       else
+               return -ENXIO;
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+       if (ret)
+               dev_err(di->dev, "Failed to kick WD!\n");
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_update_charger_current() - update charger current
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Update the charger output current for the specified charger
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
+               int ich_out)
+{
+       int ret;
+       int curr_index;
+       struct ab8500_charger *di;
+
+       if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+               di = to_ab8500_charger_ac_device_info(charger);
+       else if (charger->psy.type == POWER_SUPPLY_TYPE_USB)
+               di = to_ab8500_charger_usb_device_info(charger);
+       else
+               return -ENXIO;
+
+       curr_index = ab8500_current_to_regval(ich_out);
+       if (curr_index < 0) {
+               dev_err(di->dev,
+                       "Charger current too high, "
+                       "charging not started\n");
+               return -ENXIO;
+       }
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+       if (ret) {
+               dev_err(di->dev, "%s write failed\n", __func__);
+               return ret;
+       }
+
+       /* Reset the main and usb drop input current measurement counter */
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                               AB8500_CHARGER_CTRL,
+                               0x1);
+       if (ret) {
+               dev_err(di->dev, "%s write failed\n", __func__);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data)
+{
+       struct power_supply *psy;
+       struct power_supply *ext;
+       struct ab8500_charger *di;
+       union power_supply_propval ret;
+       int i, j;
+       bool psy_found = false;
+       struct ux500_charger *usb_chg;
+
+       usb_chg = (struct ux500_charger *)data;
+       psy = &usb_chg->psy;
+
+       di = to_ab8500_charger_usb_device_info(usb_chg);
+
+       ext = dev_get_drvdata(dev);
+
+       /* For all psy where the driver name appears in any supplied_to */
+       for (i = 0; i < ext->num_supplicants; i++) {
+               if (!strcmp(ext->supplied_to[i], psy->name))
+                       psy_found = true;
+       }
+
+       if (!psy_found)
+               return 0;
+
+       /* Go through all properties for the psy */
+       for (j = 0; j < ext->num_properties; j++) {
+               enum power_supply_property prop;
+               prop = ext->properties[j];
+
+               if (ext->get_property(ext, prop, &ret))
+                       continue;
+
+               switch (prop) {
+               case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               di->vbat = ret.intval / 1000;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+/**
+ * ab8500_charger_check_vbat_work() - keep vbus current within spec
+ * @work       pointer to the work_struct structure
+ *
+ * Due to a asic bug it is necessary to lower the input current to the vbus
+ * charger when charging with at some specific levels. This issue is only valid
+ * for below a certain battery voltage. This function makes sure that the
+ * the allowed current limit isn't exceeded.
+ */
+static void ab8500_charger_check_vbat_work(struct work_struct *work)
+{
+       int t = 10;
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_vbat_work.work);
+
+       class_for_each_device(power_supply_class, NULL,
+               &di->usb_chg.psy, ab8500_charger_get_ext_psy_data);
+
+       /* First run old_vbat is 0. */
+       if (di->old_vbat == 0)
+               di->old_vbat = di->vbat;
+
+       if (!((di->old_vbat <= VBAT_TRESH_IP_CUR_RED &&
+               di->vbat <= VBAT_TRESH_IP_CUR_RED) ||
+               (di->old_vbat > VBAT_TRESH_IP_CUR_RED &&
+               di->vbat > VBAT_TRESH_IP_CUR_RED))) {
+
+               dev_dbg(di->dev, "Vbat did cross threshold, curr: %d, new: %d,"
+                       " old: %d\n", di->max_usb_in_curr, di->vbat,
+                       di->old_vbat);
+               ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+               power_supply_changed(&di->usb_chg.psy);
+       }
+
+       di->old_vbat = di->vbat;
+
+       /*
+        * No need to check the battery voltage every second when not close to
+        * the threshold.
+        */
+       if (di->vbat < (VBAT_TRESH_IP_CUR_RED + 100) &&
+               (di->vbat > (VBAT_TRESH_IP_CUR_RED - 100)))
+                       t = 1;
+
+       queue_delayed_work(di->charger_wq, &di->check_vbat_work, t * HZ);
+}
+
+/**
+ * ab8500_charger_check_hw_failure_work() - check main charger failure
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the main charger status
+ */
+static void ab8500_charger_check_hw_failure_work(struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_hw_failure_work.work);
+
+       /* Check if the status bits for HW failure is still active */
+       if (di->flags.mainextchnotok) {
+               ret = abx500_get_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return;
+               }
+               if (!(reg_value & MAIN_CH_NOK)) {
+                       di->flags.mainextchnotok = false;
+                       ab8500_power_supply_changed(di, &di->ac_chg.psy);
+               }
+       }
+       if (di->flags.vbus_ovv) {
+               ret = abx500_get_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG,
+                       &reg_value);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return;
+               }
+               if (!(reg_value & VBUS_OVV_TH)) {
+                       di->flags.vbus_ovv = false;
+                       ab8500_power_supply_changed(di, &di->usb_chg.psy);
+               }
+       }
+       /* If we still have a failure, schedule a new check */
+       if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
+               queue_delayed_work(di->charger_wq,
+                       &di->check_hw_failure_work, round_jiffies(HZ));
+       }
+}
+
+/**
+ * ab8500_charger_kick_watchdog_work() - kick the watchdog
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for kicking the charger watchdog.
+ *
+ * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+ * logic. That means we have to continously kick the charger
+ * watchdog even when no charger is connected. This is only
+ * valid once the AC charger has been enabled. This is
+ * a bug that is not handled by the algorithm and the
+ * watchdog have to be kicked by the charger driver
+ * when the AC charger is disabled
+ */
+static void ab8500_charger_kick_watchdog_work(struct work_struct *work)
+{
+       int ret;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, kick_wd_work.work);
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+       if (ret)
+               dev_err(di->dev, "Failed to kick WD!\n");
+
+       /* Schedule a new watchdog kick */
+       queue_delayed_work(di->charger_wq,
+               &di->kick_wd_work, round_jiffies(WD_KICK_INTERVAL));
+}
+
+/**
+ * ab8500_charger_ac_work() - work to get and set main charger status
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the main charger status
+ */
+static void ab8500_charger_ac_work(struct work_struct *work)
+{
+       int ret;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, ac_work);
+
+       /*
+        * Since we can't be sure that the events are received
+        * synchronously, we have the check if the main charger is
+        * connected by reading the status register
+        */
+       ret = ab8500_charger_detect_chargers(di);
+       if (ret < 0)
+               return;
+
+       if (ret & AC_PW_CONN) {
+               di->ac.charger_connected = 1;
+               di->ac_conn = true;
+       } else {
+               di->ac.charger_connected = 0;
+       }
+
+       ab8500_power_supply_changed(di, &di->ac_chg.psy);
+       sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present");
+}
+
+/**
+ * ab8500_charger_detect_usb_type_work() - work to detect USB type
+ * @work:      Pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
+{
+       int ret;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, detect_usb_type_work);
+
+       /*
+        * Since we can't be sure that the events are received
+        * synchronously, we have the check if is
+        * connected by reading the status register
+        */
+       ret = ab8500_charger_detect_chargers(di);
+       if (ret < 0)
+               return;
+
+       if (!(ret & USB_PW_CONN)) {
+               di->vbus_detected = 0;
+               ab8500_charger_set_usb_connected(di, false);
+               ab8500_power_supply_changed(di, &di->usb_chg.psy);
+       } else {
+               di->vbus_detected = 1;
+
+               if (is_ab8500_1p1_or_earlier(di->parent)) {
+                       ret = ab8500_charger_detect_usb_type(di);
+                       if (!ret) {
+                               ab8500_charger_set_usb_connected(di, true);
+                               ab8500_power_supply_changed(di,
+                                                           &di->usb_chg.psy);
+                       }
+               } else {
+                       /* For ABB cut2.0 and onwards we have an IRQ,
+                        * USB_LINK_STATUS that will be triggered when the USB
+                        * link status changes. The exception is USB connected
+                        * during startup. Then we don't get a
+                        * USB_LINK_STATUS IRQ
+                        */
+                       if (di->vbus_detected_start) {
+                               di->vbus_detected_start = false;
+                               ret = ab8500_charger_detect_usb_type(di);
+                               if (!ret) {
+                                       ab8500_charger_set_usb_connected(di,
+                                               true);
+                                       ab8500_power_supply_changed(di,
+                                               &di->usb_chg.psy);
+                               }
+                       }
+               }
+       }
+}
+
+/**
+ * ab8500_charger_usb_link_status_work() - work to detect USB type
+ * @work:      pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_usb_link_status_work(struct work_struct *work)
+{
+       int ret;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, usb_link_status_work);
+
+       /*
+        * Since we can't be sure that the events are received
+        * synchronously, we have the check if  is
+        * connected by reading the status register
+        */
+       ret = ab8500_charger_detect_chargers(di);
+       if (ret < 0)
+               return;
+
+       if (!(ret & USB_PW_CONN)) {
+               di->vbus_detected = 0;
+               ab8500_charger_set_usb_connected(di, false);
+               ab8500_power_supply_changed(di, &di->usb_chg.psy);
+       } else {
+               di->vbus_detected = 1;
+               ret = ab8500_charger_read_usb_type(di);
+               if (!ret) {
+                       /* Update maximum input current */
+                       ret = ab8500_charger_set_vbus_in_curr(di,
+                                       di->max_usb_in_curr);
+                       if (ret)
+                               return;
+
+                       ab8500_charger_set_usb_connected(di, true);
+                       ab8500_power_supply_changed(di, &di->usb_chg.psy);
+               } else if (ret == -ENXIO) {
+                       /* No valid charger type detected */
+                       ab8500_charger_set_usb_connected(di, false);
+                       ab8500_power_supply_changed(di, &di->usb_chg.psy);
+               }
+       }
+}
+
+static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
+{
+       int ret;
+       unsigned long flags;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, usb_state_changed_work);
+
+       if (!di->vbus_detected)
+               return;
+
+       spin_lock_irqsave(&di->usb_state.usb_lock, flags);
+       di->usb_state.usb_changed = false;
+       spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
+
+       /*
+        * wait for some time until you get updates from the usb stack
+        * and negotiations are completed
+        */
+       msleep(250);
+
+       if (di->usb_state.usb_changed)
+               return;
+
+       dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
+               __func__, di->usb_state.state, di->usb_state.usb_current);
+
+       switch (di->usb_state.state) {
+       case AB8500_BM_USB_STATE_RESET_HS:
+       case AB8500_BM_USB_STATE_RESET_FS:
+       case AB8500_BM_USB_STATE_SUSPEND:
+       case AB8500_BM_USB_STATE_MAX:
+               ab8500_charger_set_usb_connected(di, false);
+               ab8500_power_supply_changed(di, &di->usb_chg.psy);
+               break;
+
+       case AB8500_BM_USB_STATE_RESUME:
+               /*
+                * when suspend->resume there should be delay
+                * of 1sec for enabling charging
+                */
+               msleep(1000);
+               /* Intentional fall through */
+       case AB8500_BM_USB_STATE_CONFIGURED:
+               /*
+                * USB is configured, enable charging with the charging
+                * input current obtained from USB driver
+                */
+               if (!ab8500_charger_get_usb_cur(di)) {
+                       /* Update maximum input current */
+                       ret = ab8500_charger_set_vbus_in_curr(di,
+                                       di->max_usb_in_curr);
+                       if (ret)
+                               return;
+
+                       ab8500_charger_set_usb_connected(di, true);
+                       ab8500_power_supply_changed(di, &di->usb_chg.psy);
+               }
+               break;
+
+       default:
+               break;
+       };
+}
+
+/**
+ * ab8500_charger_check_usbchargernotok_work() - check USB chg not ok status
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB charger Not OK status
+ */
+static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+       bool prev_status;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_usbchgnotok_work.work);
+
+       /* Check if the status bit for usbchargernotok is still active */
+       ret = abx500_get_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return;
+       }
+       prev_status = di->flags.usbchargernotok;
+
+       if (reg_value & VBUS_CH_NOK) {
+               di->flags.usbchargernotok = true;
+               /* Check again in 1sec */
+               queue_delayed_work(di->charger_wq,
+                       &di->check_usbchgnotok_work, HZ);
+       } else {
+               di->flags.usbchargernotok = false;
+               di->flags.vbus_collapse = false;
+       }
+
+       if (prev_status != di->flags.usbchargernotok)
+               ab8500_power_supply_changed(di, &di->usb_chg.psy);
+}
+
+/**
+ * ab8500_charger_check_main_thermal_prot_work() - check main thermal status
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the Main thermal prot status
+ */
+static void ab8500_charger_check_main_thermal_prot_work(
+       struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_main_thermal_prot_work);
+
+       /* Check if the status bit for main_thermal_prot is still active */
+       ret = abx500_get_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return;
+       }
+       if (reg_value & MAIN_CH_TH_PROT)
+               di->flags.main_thermal_prot = true;
+       else
+               di->flags.main_thermal_prot = false;
+
+       ab8500_power_supply_changed(di, &di->ac_chg.psy);
+}
+
+/**
+ * ab8500_charger_check_usb_thermal_prot_work() - check usb thermal status
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB thermal prot status
+ */
+static void ab8500_charger_check_usb_thermal_prot_work(
+       struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_usb_thermal_prot_work);
+
+       /* Check if the status bit for usb_thermal_prot is still active */
+       ret = abx500_get_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return;
+       }
+       if (reg_value & USB_CH_TH_PROT)
+               di->flags.usb_thermal_prot = true;
+       else
+               di->flags.usb_thermal_prot = false;
+
+       ab8500_power_supply_changed(di, &di->usb_chg.psy);
+}
+
+/**
+ * ab8500_charger_mainchunplugdet_handler() - main charger unplugged
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchunplugdet_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Main charger unplugged\n");
+       queue_work(di->charger_wq, &di->ac_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchplugdet_handler() - main charger plugged
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Main charger plugged\n");
+       queue_work(di->charger_wq, &di->ac_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainextchnotok_handler() - main charger not ok
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainextchnotok_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Main charger not ok\n");
+       di->flags.mainextchnotok = true;
+       ab8500_power_supply_changed(di, &di->ac_chg.psy);
+
+       /* Schedule a new HW failure check */
+       queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchthprotr_handler() - Die temp is above main charger
+ * thermal protection threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchthprotr_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev,
+               "Die temp above Main charger thermal protection threshold\n");
+       queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchthprotf_handler() - Die temp is below main charger
+ * thermal protection threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev,
+               "Die temp ok for Main charger thermal protection threshold\n");
+       queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusdetf_handler() - VBUS falling detected
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "VBUS falling detected\n");
+       queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusdetr_handler() - VBUS rising detected
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusdetr_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       di->vbus_detected = true;
+       dev_dbg(di->dev, "VBUS rising detected\n");
+       queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usblinkstatus_handler() - USB link status has changed
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usblinkstatus_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "USB link status changed\n");
+
+       queue_work(di->charger_wq, &di->usb_link_status_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchthprotr_handler() - Die temp is above usb charger
+ * thermal protection threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchthprotr_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev,
+               "Die temp above USB charger thermal protection threshold\n");
+       queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchthprotf_handler() - Die temp is below usb charger
+ * thermal protection threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchthprotf_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev,
+               "Die temp ok for USB charger thermal protection threshold\n");
+       queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchargernotokr_handler() - USB charger not ok detected
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchargernotokr_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Not allowed USB charger detected\n");
+       queue_delayed_work(di->charger_wq, &di->check_usbchgnotok_work, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_chwdexp_handler() - Charger watchdog expired
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Charger watchdog expired\n");
+
+       /*
+        * The charger that was online when the watchdog expired
+        * needs to be restarted for charging to start again
+        */
+       if (di->ac.charger_online) {
+               di->ac.wd_expired = true;
+               ab8500_power_supply_changed(di, &di->ac_chg.psy);
+       }
+       if (di->usb.charger_online) {
+               di->usb.wd_expired = true;
+               ab8500_power_supply_changed(di, &di->usb_chg.psy);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusovv_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "VBUS overvoltage detected\n");
+       di->flags.vbus_ovv = true;
+       ab8500_power_supply_changed(di, &di->usb_chg.psy);
+
+       /* Schedule a new HW failure check */
+       queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_ac_get_property() - get the ac/mains properties
+ * @psy:       pointer to the power_supply structure
+ * @psp:       pointer to the power_supply_property structure
+ * @val:       pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the ac/mains
+ * properties by reading the sysfs files.
+ * AC/Mains properties are online, present and voltage.
+ * online:     ac/mains charging is in progress or not
+ * present:    presence of the ac/mains
+ * voltage:    AC/Mains voltage
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_ac_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct ab8500_charger *di;
+
+       di = to_ab8500_charger_ac_device_info(psy_to_ux500_charger(psy));
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (di->flags.mainextchnotok)
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               else if (di->ac.wd_expired || di->usb.wd_expired)
+                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
+               else if (di->flags.main_thermal_prot)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = di->ac.charger_online;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = di->ac.charger_connected;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               di->ac.charger_voltage = ab8500_charger_get_ac_voltage(di);
+               val->intval = di->ac.charger_voltage * 1000;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               /*
+                * This property is used to indicate when CV mode is entered
+                * for the AC charger
+                */
+               di->ac.cv_active = ab8500_charger_ac_cv(di);
+               val->intval = di->ac.cv_active;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = ab8500_charger_get_ac_current(di) * 1000;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/**
+ * ab8500_charger_usb_get_property() - get the usb properties
+ * @psy:        pointer to the power_supply structure
+ * @psp:        pointer to the power_supply_property structure
+ * @val:        pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the usb
+ * properties by reading the sysfs files.
+ * USB properties are online, present and voltage.
+ * online:     usb charging is in progress or not
+ * present:    presence of the usb
+ * voltage:    vbus voltage
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_usb_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct ab8500_charger *di;
+
+       di = to_ab8500_charger_usb_device_info(psy_to_ux500_charger(psy));
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (di->flags.usbchargernotok)
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               else if (di->ac.wd_expired || di->usb.wd_expired)
+                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
+               else if (di->flags.usb_thermal_prot)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else if (di->flags.vbus_ovv)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = di->usb.charger_online;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = di->usb.charger_connected;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               di->usb.charger_voltage = ab8500_charger_get_vbus_voltage(di);
+               val->intval = di->usb.charger_voltage * 1000;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               /*
+                * This property is used to indicate when CV mode is entered
+                * for the USB charger
+                */
+               di->usb.cv_active = ab8500_charger_usb_cv(di);
+               val->intval = di->usb.cv_active;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = ab8500_charger_get_usb_current(di) * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               /*
+                * This property is used to indicate when VBUS has collapsed
+                * due to too high output current from the USB charger
+                */
+               if (di->flags.vbus_collapse)
+                       val->intval = 1;
+               else
+                       val->intval = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/**
+ * ab8500_charger_init_hw_registers() - Set up charger related registers
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Set up charger OVV, watchdog and maximum voltage registers as well as
+ * charging of the backup battery
+ */
+static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
+{
+       int ret = 0;
+
+       /* Setup maximum charger current and voltage for ABB cut2.0 */
+       if (!is_ab8500_1p1_or_earlier(di->parent)) {
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_CHARGER,
+                       AB8500_CH_VOLT_LVL_MAX_REG, CH_VOL_LVL_4P6);
+               if (ret) {
+                       dev_err(di->dev,
+                               "failed to set CH_VOLT_LVL_MAX_REG\n");
+                       goto out;
+               }
+
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_CHARGER,
+                       AB8500_CH_OPT_CRNTLVL_MAX_REG, CH_OP_CUR_LVL_1P6);
+               if (ret) {
+                       dev_err(di->dev,
+                               "failed to set CH_OPT_CRNTLVL_MAX_REG\n");
+                       goto out;
+               }
+       }
+
+       /* VBUS OVV set to 6.3V and enable automatic current limitiation */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_CHARGER,
+               AB8500_USBCH_CTRL2_REG,
+               VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
+       if (ret) {
+               dev_err(di->dev, "failed to set VBUS OVV\n");
+               goto out;
+       }
+
+       /* Enable main watchdog in OTP */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_OTP_EMUL, AB8500_OTP_CONF_15, OTP_ENABLE_WD);
+       if (ret) {
+               dev_err(di->dev, "failed to enable main WD in OTP\n");
+               goto out;
+       }
+
+       /* Enable main watchdog */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_SYS_CTRL2_BLOCK,
+               AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_ENA);
+       if (ret) {
+               dev_err(di->dev, "faile to enable main watchdog\n");
+               goto out;
+       }
+
+       /*
+        * Due to internal synchronisation, Enable and Kick watchdog bits
+        * cannot be enabled in a single write.
+        * A minimum delay of 2*32 kHz period (62.5µs) must be inserted
+        * between writing Enable then Kick bits.
+        */
+       udelay(63);
+
+       /* Kick main watchdog */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_SYS_CTRL2_BLOCK,
+               AB8500_MAIN_WDOG_CTRL_REG,
+               (MAIN_WDOG_ENA | MAIN_WDOG_KICK));
+       if (ret) {
+               dev_err(di->dev, "failed to kick main watchdog\n");
+               goto out;
+       }
+
+       /* Disable main watchdog */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_SYS_CTRL2_BLOCK,
+               AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_DIS);
+       if (ret) {
+               dev_err(di->dev, "failed to disable main watchdog\n");
+               goto out;
+       }
+
+       /* Set watchdog timeout */
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CH_WD_TIMER_REG, WD_TIMER);
+       if (ret) {
+               dev_err(di->dev, "failed to set charger watchdog timeout\n");
+               goto out;
+       }
+
+       /* Backup battery voltage and current */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_RTC,
+               AB8500_RTC_BACKUP_CHG_REG,
+               di->bat->bkup_bat_v |
+               di->bat->bkup_bat_i);
+       if (ret) {
+               dev_err(di->dev, "failed to setup backup battery charging\n");
+               goto out;
+       }
+
+       /* Enable backup battery charging */
+       abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_RTC, AB8500_RTC_CTRL_REG,
+               RTC_BUP_CH_ENA, RTC_BUP_CH_ENA);
+       if (ret < 0)
+               dev_err(di->dev, "%s mask and set failed\n", __func__);
+
+out:
+       return ret;
+}
+
+/*
+ * ab8500 charger driver interrupts and their respective isr
+ */
+static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
+       {"MAIN_CH_UNPLUG_DET", ab8500_charger_mainchunplugdet_handler},
+       {"MAIN_CHARGE_PLUG_DET", ab8500_charger_mainchplugdet_handler},
+       {"MAIN_EXT_CH_NOT_OK", ab8500_charger_mainextchnotok_handler},
+       {"MAIN_CH_TH_PROT_R", ab8500_charger_mainchthprotr_handler},
+       {"MAIN_CH_TH_PROT_F", ab8500_charger_mainchthprotf_handler},
+       {"VBUS_DET_F", ab8500_charger_vbusdetf_handler},
+       {"VBUS_DET_R", ab8500_charger_vbusdetr_handler},
+       {"USB_LINK_STATUS", ab8500_charger_usblinkstatus_handler},
+       {"USB_CH_TH_PROT_R", ab8500_charger_usbchthprotr_handler},
+       {"USB_CH_TH_PROT_F", ab8500_charger_usbchthprotf_handler},
+       {"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
+       {"VBUS_OVV", ab8500_charger_vbusovv_handler},
+       {"CH_WD_EXP", ab8500_charger_chwdexp_handler},
+};
+
+static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
+               unsigned long event, void *power)
+{
+       struct ab8500_charger *di =
+               container_of(nb, struct ab8500_charger, nb);
+       enum ab8500_usb_state bm_usb_state;
+       unsigned mA = *((unsigned *)power);
+
+       if (event != USB_EVENT_VBUS) {
+               dev_dbg(di->dev, "not a standard host, returning\n");
+               return NOTIFY_DONE;
+       }
+
+       /* TODO: State is fabricate  here. See if charger really needs USB
+        * state or if mA is enough
+        */
+       if ((di->usb_state.usb_current == 2) && (mA > 2))
+               bm_usb_state = AB8500_BM_USB_STATE_RESUME;
+       else if (mA == 0)
+               bm_usb_state = AB8500_BM_USB_STATE_RESET_HS;
+       else if (mA == 2)
+               bm_usb_state = AB8500_BM_USB_STATE_SUSPEND;
+       else if (mA >= 8) /* 8, 100, 500 */
+               bm_usb_state = AB8500_BM_USB_STATE_CONFIGURED;
+       else /* Should never occur */
+               bm_usb_state = AB8500_BM_USB_STATE_RESET_FS;
+
+       dev_dbg(di->dev, "%s usb_state: 0x%02x mA: %d\n",
+               __func__, bm_usb_state, mA);
+
+       spin_lock(&di->usb_state.usb_lock);
+       di->usb_state.usb_changed = true;
+       spin_unlock(&di->usb_state.usb_lock);
+
+       di->usb_state.state = bm_usb_state;
+       di->usb_state.usb_current = mA;
+
+       queue_work(di->charger_wq, &di->usb_state_changed_work);
+
+       return NOTIFY_OK;
+}
+
+#if defined(CONFIG_PM)
+static int ab8500_charger_resume(struct platform_device *pdev)
+{
+       int ret;
+       struct ab8500_charger *di = platform_get_drvdata(pdev);
+
+       /*
+        * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+        * logic. That means we have to continously kick the charger
+        * watchdog even when no charger is connected. This is only
+        * valid once the AC charger has been enabled. This is
+        * a bug that is not handled by the algorithm and the
+        * watchdog have to be kicked by the charger driver
+        * when the AC charger is disabled
+        */
+       if (di->ac_conn && is_ab8500_1p1_or_earlier(di->parent)) {
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+               if (ret)
+                       dev_err(di->dev, "Failed to kick WD!\n");
+
+               /* If not already pending start a new timer */
+               if (!delayed_work_pending(
+                       &di->kick_wd_work)) {
+                       queue_delayed_work(di->charger_wq, &di->kick_wd_work,
+                               round_jiffies(WD_KICK_INTERVAL));
+               }
+       }
+
+       /* If we still have a HW failure, schedule a new check */
+       if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
+               queue_delayed_work(di->charger_wq,
+                       &di->check_hw_failure_work, 0);
+       }
+
+       return 0;
+}
+
+static int ab8500_charger_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct ab8500_charger *di = platform_get_drvdata(pdev);
+
+       /* Cancel any pending HW failure check */
+       if (delayed_work_pending(&di->check_hw_failure_work))
+               cancel_delayed_work(&di->check_hw_failure_work);
+
+       return 0;
+}
+#else
+#define ab8500_charger_suspend      NULL
+#define ab8500_charger_resume       NULL
+#endif
+
+static int __devexit ab8500_charger_remove(struct platform_device *pdev)
+{
+       struct ab8500_charger *di = platform_get_drvdata(pdev);
+       int i, irq, ret;
+
+       /* Disable AC charging */
+       ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
+
+       /* Disable USB charging */
+       ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
+
+       /* Disable interrupts */
+       for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+               free_irq(irq, di);
+       }
+
+       /* disable the regulator */
+       regulator_put(di->regu);
+
+       /* Backup battery voltage and current disable */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0);
+       if (ret < 0)
+               dev_err(di->dev, "%s mask and set failed\n", __func__);
+
+       usb_unregister_notifier(di->usb_phy, &di->nb);
+       usb_put_transceiver(di->usb_phy);
+
+       /* Delete the work queue */
+       destroy_workqueue(di->charger_wq);
+
+       flush_scheduled_work();
+       power_supply_unregister(&di->usb_chg.psy);
+       power_supply_unregister(&di->ac_chg.psy);
+       platform_set_drvdata(pdev, NULL);
+       kfree(di);
+
+       return 0;
+}
+
+static int __devinit ab8500_charger_probe(struct platform_device *pdev)
+{
+       int irq, i, charger_status, ret = 0;
+       struct abx500_bm_plat_data *plat_data;
+
+       struct ab8500_charger *di =
+               kzalloc(sizeof(struct ab8500_charger), GFP_KERNEL);
+       if (!di)
+               return -ENOMEM;
+
+       /* get parent data */
+       di->dev = &pdev->dev;
+       di->parent = dev_get_drvdata(pdev->dev.parent);
+       di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+       /* initialize lock */
+       spin_lock_init(&di->usb_state.usb_lock);
+
+       /* get charger specific platform data */
+       plat_data = pdev->dev.platform_data;
+       di->pdata = plat_data->charger;
+
+       if (!di->pdata) {
+               dev_err(di->dev, "no charger platform data supplied\n");
+               ret = -EINVAL;
+               goto free_device_info;
+       }
+
+       /* get battery specific platform data */
+       di->bat = plat_data->battery;
+       if (!di->bat) {
+               dev_err(di->dev, "no battery platform data supplied\n");
+               ret = -EINVAL;
+               goto free_device_info;
+       }
+
+       di->autopower = false;
+
+       /* AC supply */
+       /* power_supply base class */
+       di->ac_chg.psy.name = "ab8500_ac";
+       di->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS;
+       di->ac_chg.psy.properties = ab8500_charger_ac_props;
+       di->ac_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_ac_props);
+       di->ac_chg.psy.get_property = ab8500_charger_ac_get_property;
+       di->ac_chg.psy.supplied_to = di->pdata->supplied_to;
+       di->ac_chg.psy.num_supplicants = di->pdata->num_supplicants;
+       /* ux500_charger sub-class */
+       di->ac_chg.ops.enable = &ab8500_charger_ac_en;
+       di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
+       di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current;
+       di->ac_chg.max_out_volt = ab8500_charger_voltage_map[
+               ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
+       di->ac_chg.max_out_curr = ab8500_charger_current_map[
+               ARRAY_SIZE(ab8500_charger_current_map) - 1];
+
+       /* USB supply */
+       /* power_supply base class */
+       di->usb_chg.psy.name = "ab8500_usb";
+       di->usb_chg.psy.type = POWER_SUPPLY_TYPE_USB;
+       di->usb_chg.psy.properties = ab8500_charger_usb_props;
+       di->usb_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_usb_props);
+       di->usb_chg.psy.get_property = ab8500_charger_usb_get_property;
+       di->usb_chg.psy.supplied_to = di->pdata->supplied_to;
+       di->usb_chg.psy.num_supplicants = di->pdata->num_supplicants;
+       /* ux500_charger sub-class */
+       di->usb_chg.ops.enable = &ab8500_charger_usb_en;
+       di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
+       di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current;
+       di->usb_chg.max_out_volt = ab8500_charger_voltage_map[
+               ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
+       di->usb_chg.max_out_curr = ab8500_charger_current_map[
+               ARRAY_SIZE(ab8500_charger_current_map) - 1];
+
+
+       /* Create a work queue for the charger */
+       di->charger_wq =
+               create_singlethread_workqueue("ab8500_charger_wq");
+       if (di->charger_wq == NULL) {
+               dev_err(di->dev, "failed to create work queue\n");
+               goto free_device_info;
+       }
+
+       /* Init work for HW failure check */
+       INIT_DELAYED_WORK_DEFERRABLE(&di->check_hw_failure_work,
+               ab8500_charger_check_hw_failure_work);
+       INIT_DELAYED_WORK_DEFERRABLE(&di->check_usbchgnotok_work,
+               ab8500_charger_check_usbchargernotok_work);
+
+       /*
+        * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+        * logic. That means we have to continously kick the charger
+        * watchdog even when no charger is connected. This is only
+        * valid once the AC charger has been enabled. This is
+        * a bug that is not handled by the algorithm and the
+        * watchdog have to be kicked by the charger driver
+        * when the AC charger is disabled
+        */
+       INIT_DELAYED_WORK_DEFERRABLE(&di->kick_wd_work,
+               ab8500_charger_kick_watchdog_work);
+
+       INIT_DELAYED_WORK_DEFERRABLE(&di->check_vbat_work,
+               ab8500_charger_check_vbat_work);
+
+       /* Init work for charger detection */
+       INIT_WORK(&di->usb_link_status_work,
+               ab8500_charger_usb_link_status_work);
+       INIT_WORK(&di->ac_work, ab8500_charger_ac_work);
+       INIT_WORK(&di->detect_usb_type_work,
+               ab8500_charger_detect_usb_type_work);
+
+       INIT_WORK(&di->usb_state_changed_work,
+               ab8500_charger_usb_state_changed_work);
+
+       /* Init work for checking HW status */
+       INIT_WORK(&di->check_main_thermal_prot_work,
+               ab8500_charger_check_main_thermal_prot_work);
+       INIT_WORK(&di->check_usb_thermal_prot_work,
+               ab8500_charger_check_usb_thermal_prot_work);
+
+       /*
+        * VDD ADC supply needs to be enabled from this driver when there
+        * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
+        * interrupts during charging
+        */
+       di->regu = regulator_get(di->dev, "vddadc");
+       if (IS_ERR(di->regu)) {
+               ret = PTR_ERR(di->regu);
+               dev_err(di->dev, "failed to get vddadc regulator\n");
+               goto free_charger_wq;
+       }
+
+
+       /* Initialize OVV, and other registers */
+       ret = ab8500_charger_init_hw_registers(di);
+       if (ret) {
+               dev_err(di->dev, "failed to initialize ABB registers\n");
+               goto free_regulator;
+       }
+
+       /* Register AC charger class */
+       ret = power_supply_register(di->dev, &di->ac_chg.psy);
+       if (ret) {
+               dev_err(di->dev, "failed to register AC charger\n");
+               goto free_regulator;
+       }
+
+       /* Register USB charger class */
+       ret = power_supply_register(di->dev, &di->usb_chg.psy);
+       if (ret) {
+               dev_err(di->dev, "failed to register USB charger\n");
+               goto free_ac;
+       }
+
+       di->usb_phy = usb_get_transceiver();
+       if (!di->usb_phy) {
+               dev_err(di->dev, "failed to get usb transceiver\n");
+               ret = -EINVAL;
+               goto free_usb;
+       }
+       di->nb.notifier_call = ab8500_charger_usb_notifier_call;
+       ret = usb_register_notifier(di->usb_phy, &di->nb);
+       if (ret) {
+               dev_err(di->dev, "failed to register usb notifier\n");
+               goto put_usb_phy;
+       }
+
+       /* Identify the connected charger types during startup */
+       charger_status = ab8500_charger_detect_chargers(di);
+       if (charger_status & AC_PW_CONN) {
+               di->ac.charger_connected = 1;
+               di->ac_conn = true;
+               ab8500_power_supply_changed(di, &di->ac_chg.psy);
+               sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present");
+       }
+
+       if (charger_status & USB_PW_CONN) {
+               dev_dbg(di->dev, "VBUS Detect during startup\n");
+               di->vbus_detected = true;
+               di->vbus_detected_start = true;
+               queue_work(di->charger_wq,
+                       &di->detect_usb_type_work);
+       }
+
+       /* Register interrupts */
+       for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+               ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr,
+                       IRQF_SHARED | IRQF_NO_SUSPEND,
+                       ab8500_charger_irq[i].name, di);
+
+               if (ret != 0) {
+                       dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+                               , ab8500_charger_irq[i].name, irq, ret);
+                       goto free_irq;
+               }
+               dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+                       ab8500_charger_irq[i].name, irq, ret);
+       }
+
+       platform_set_drvdata(pdev, di);
+
+       return ret;
+
+free_irq:
+       usb_unregister_notifier(di->usb_phy, &di->nb);
+
+       /* We also have to free all successfully registered irqs */
+       for (i = i - 1; i >= 0; i--) {
+               irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+               free_irq(irq, di);
+       }
+put_usb_phy:
+       usb_put_transceiver(di->usb_phy);
+free_usb:
+       power_supply_unregister(&di->usb_chg.psy);
+free_ac:
+       power_supply_unregister(&di->ac_chg.psy);
+free_regulator:
+       regulator_put(di->regu);
+free_charger_wq:
+       destroy_workqueue(di->charger_wq);
+free_device_info:
+       kfree(di);
+
+       return ret;
+}
+
+static struct platform_driver ab8500_charger_driver = {
+       .probe = ab8500_charger_probe,
+       .remove = __devexit_p(ab8500_charger_remove),
+       .suspend = ab8500_charger_suspend,
+       .resume = ab8500_charger_resume,
+       .driver = {
+               .name = "ab8500-charger",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init ab8500_charger_init(void)
+{
+       return platform_driver_register(&ab8500_charger_driver);
+}
+
+static void __exit ab8500_charger_exit(void)
+{
+       platform_driver_unregister(&ab8500_charger_driver);
+}
+
+subsys_initcall_sync(ab8500_charger_init);
+module_exit(ab8500_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
+MODULE_ALIAS("platform:ab8500-charger");
+MODULE_DESCRIPTION("AB8500 charger management driver");
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
new file mode 100644 (file)
index 0000000..c22f2f0
--- /dev/null
@@ -0,0 +1,2637 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2012
+ *
+ * Main and Back-up battery management driver.
+ *
+ * Note: Backup battery management is required in case of Li-Ion battery and not
+ * for capacitive battery. HREF boards have capacitive battery and hence backup
+ * battery management is not used and the supported code is available in this
+ * driver.
+ *
+ * License Terms: GNU General Public License v2
+ * Author:
+ *     Johan Palsson <johan.palsson@stericsson.com>
+ *     Karl Komierowski <karl.komierowski@stericsson.com>
+ *     Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/kobject.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/slab.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/delay.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500.h>
+#include <linux/time.h>
+#include <linux/completion.h>
+
+#define MILLI_TO_MICRO                 1000
+#define FG_LSB_IN_MA                   1627
+#define QLSB_NANO_AMP_HOURS_X10                1129
+#define INS_CURR_TIMEOUT               (3 * HZ)
+
+#define SEC_TO_SAMPLE(S)               (S * 4)
+
+#define NBR_AVG_SAMPLES                        20
+
+#define LOW_BAT_CHECK_INTERVAL         (2 * HZ)
+
+#define VALID_CAPACITY_SEC             (45 * 60) /* 45 minutes */
+#define BATT_OK_MIN                    2360 /* mV */
+#define BATT_OK_INCREMENT              50 /* mV */
+#define BATT_OK_MAX_NR_INCREMENTS      0xE
+
+/* FG constants */
+#define BATT_OVV                       0x01
+
+#define interpolate(x, x1, y1, x2, y2) \
+       ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
+
+#define to_ab8500_fg_device_info(x) container_of((x), \
+       struct ab8500_fg, fg_psy);
+
+/**
+ * struct ab8500_fg_interrupts - ab8500 fg interupts
+ * @name:      name of the interrupt
+ * @isr                function pointer to the isr
+ */
+struct ab8500_fg_interrupts {
+       char *name;
+       irqreturn_t (*isr)(int irq, void *data);
+};
+
+enum ab8500_fg_discharge_state {
+       AB8500_FG_DISCHARGE_INIT,
+       AB8500_FG_DISCHARGE_INITMEASURING,
+       AB8500_FG_DISCHARGE_INIT_RECOVERY,
+       AB8500_FG_DISCHARGE_RECOVERY,
+       AB8500_FG_DISCHARGE_READOUT_INIT,
+       AB8500_FG_DISCHARGE_READOUT,
+       AB8500_FG_DISCHARGE_WAKEUP,
+};
+
+static char *discharge_state[] = {
+       "DISCHARGE_INIT",
+       "DISCHARGE_INITMEASURING",
+       "DISCHARGE_INIT_RECOVERY",
+       "DISCHARGE_RECOVERY",
+       "DISCHARGE_READOUT_INIT",
+       "DISCHARGE_READOUT",
+       "DISCHARGE_WAKEUP",
+};
+
+enum ab8500_fg_charge_state {
+       AB8500_FG_CHARGE_INIT,
+       AB8500_FG_CHARGE_READOUT,
+};
+
+static char *charge_state[] = {
+       "CHARGE_INIT",
+       "CHARGE_READOUT",
+};
+
+enum ab8500_fg_calibration_state {
+       AB8500_FG_CALIB_INIT,
+       AB8500_FG_CALIB_WAIT,
+       AB8500_FG_CALIB_END,
+};
+
+struct ab8500_fg_avg_cap {
+       int avg;
+       int samples[NBR_AVG_SAMPLES];
+       __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
+       int pos;
+       int nbr_samples;
+       int sum;
+};
+
+struct ab8500_fg_battery_capacity {
+       int max_mah_design;
+       int max_mah;
+       int mah;
+       int permille;
+       int level;
+       int prev_mah;
+       int prev_percent;
+       int prev_level;
+       int user_mah;
+};
+
+struct ab8500_fg_flags {
+       bool fg_enabled;
+       bool conv_done;
+       bool charging;
+       bool fully_charged;
+       bool force_full;
+       bool low_bat_delay;
+       bool low_bat;
+       bool bat_ovv;
+       bool batt_unknown;
+       bool calibrate;
+       bool user_cap;
+       bool batt_id_received;
+};
+
+struct inst_curr_result_list {
+       struct list_head list;
+       int *result;
+};
+
+/**
+ * struct ab8500_fg - ab8500 FG device information
+ * @dev:               Pointer to the structure device
+ * @node:              a list of AB8500 FGs, hence prepared for reentrance
+ * @irq                        holds the CCEOC interrupt number
+ * @vbat:              Battery voltage in mV
+ * @vbat_nom:          Nominal battery voltage in mV
+ * @inst_curr:         Instantenous battery current in mA
+ * @avg_curr:          Average battery current in mA
+ * @bat_temp           battery temperature
+ * @fg_samples:                Number of samples used in the FG accumulation
+ * @accu_charge:       Accumulated charge from the last conversion
+ * @recovery_cnt:      Counter for recovery mode
+ * @high_curr_cnt:     Counter for high current mode
+ * @init_cnt:          Counter for init mode
+ * @recovery_needed:   Indicate if recovery is needed
+ * @high_curr_mode:    Indicate if we're in high current mode
+ * @init_capacity:     Indicate if initial capacity measuring should be done
+ * @turn_off_fg:       True if fg was off before current measurement
+ * @calib_state                State during offset calibration
+ * @discharge_state:   Current discharge state
+ * @charge_state:      Current charge state
+ * @ab8500_fg_complete Completion struct used for the instant current reading
+ * @flags:             Structure for information about events triggered
+ * @bat_cap:           Structure for battery capacity specific parameters
+ * @avg_cap:           Average capacity filter
+ * @parent:            Pointer to the struct ab8500
+ * @gpadc:             Pointer to the struct gpadc
+ * @pdata:             Pointer to the abx500_fg platform data
+ * @bat:               Pointer to the abx500_bm platform data
+ * @fg_psy:            Structure that holds the FG specific battery properties
+ * @fg_wq:             Work queue for running the FG algorithm
+ * @fg_periodic_work:  Work to run the FG algorithm periodically
+ * @fg_low_bat_work:   Work to check low bat condition
+ * @fg_reinit_work     Work used to reset and reinitialise the FG algorithm
+ * @fg_work:           Work to run the FG algorithm instantly
+ * @fg_acc_cur_work:   Work to read the FG accumulator
+ * @fg_check_hw_failure_work:  Work for checking HW state
+ * @cc_lock:           Mutex for locking the CC
+ * @fg_kobject:                Structure of type kobject
+ */
+struct ab8500_fg {
+       struct device *dev;
+       struct list_head node;
+       int irq;
+       int vbat;
+       int vbat_nom;
+       int inst_curr;
+       int avg_curr;
+       int bat_temp;
+       int fg_samples;
+       int accu_charge;
+       int recovery_cnt;
+       int high_curr_cnt;
+       int init_cnt;
+       bool recovery_needed;
+       bool high_curr_mode;
+       bool init_capacity;
+       bool turn_off_fg;
+       enum ab8500_fg_calibration_state calib_state;
+       enum ab8500_fg_discharge_state discharge_state;
+       enum ab8500_fg_charge_state charge_state;
+       struct completion ab8500_fg_complete;
+       struct ab8500_fg_flags flags;
+       struct ab8500_fg_battery_capacity bat_cap;
+       struct ab8500_fg_avg_cap avg_cap;
+       struct ab8500 *parent;
+       struct ab8500_gpadc *gpadc;
+       struct abx500_fg_platform_data *pdata;
+       struct abx500_bm_data *bat;
+       struct power_supply fg_psy;
+       struct workqueue_struct *fg_wq;
+       struct delayed_work fg_periodic_work;
+       struct delayed_work fg_low_bat_work;
+       struct delayed_work fg_reinit_work;
+       struct work_struct fg_work;
+       struct work_struct fg_acc_cur_work;
+       struct delayed_work fg_check_hw_failure_work;
+       struct mutex cc_lock;
+       struct kobject fg_kobject;
+};
+static LIST_HEAD(ab8500_fg_list);
+
+/**
+ * ab8500_fg_get() - returns a reference to the primary AB8500 fuel gauge
+ * (i.e. the first fuel gauge in the instance list)
+ */
+struct ab8500_fg *ab8500_fg_get(void)
+{
+       struct ab8500_fg *fg;
+
+       if (list_empty(&ab8500_fg_list))
+               return NULL;
+
+       fg = list_first_entry(&ab8500_fg_list, struct ab8500_fg, node);
+       return fg;
+}
+
+/* Main battery properties */
+static enum power_supply_property ab8500_fg_props[] = {
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+       POWER_SUPPLY_PROP_ENERGY_FULL,
+       POWER_SUPPLY_PROP_ENERGY_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+};
+
+/*
+ * This array maps the raw hex value to lowbat voltage used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_fg_lowbat_voltage_map[] = {
+       2300 ,
+       2325 ,
+       2350 ,
+       2375 ,
+       2400 ,
+       2425 ,
+       2450 ,
+       2475 ,
+       2500 ,
+       2525 ,
+       2550 ,
+       2575 ,
+       2600 ,
+       2625 ,
+       2650 ,
+       2675 ,
+       2700 ,
+       2725 ,
+       2750 ,
+       2775 ,
+       2800 ,
+       2825 ,
+       2850 ,
+       2875 ,
+       2900 ,
+       2925 ,
+       2950 ,
+       2975 ,
+       3000 ,
+       3025 ,
+       3050 ,
+       3075 ,
+       3100 ,
+       3125 ,
+       3150 ,
+       3175 ,
+       3200 ,
+       3225 ,
+       3250 ,
+       3275 ,
+       3300 ,
+       3325 ,
+       3350 ,
+       3375 ,
+       3400 ,
+       3425 ,
+       3450 ,
+       3475 ,
+       3500 ,
+       3525 ,
+       3550 ,
+       3575 ,
+       3600 ,
+       3625 ,
+       3650 ,
+       3675 ,
+       3700 ,
+       3725 ,
+       3750 ,
+       3775 ,
+       3800 ,
+       3825 ,
+       3850 ,
+       3850 ,
+};
+
+static u8 ab8500_volt_to_regval(int voltage)
+{
+       int i;
+
+       if (voltage < ab8500_fg_lowbat_voltage_map[0])
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(ab8500_fg_lowbat_voltage_map); i++) {
+               if (voltage < ab8500_fg_lowbat_voltage_map[i])
+                       return (u8) i - 1;
+       }
+
+       /* If not captured above, return index of last element */
+       return (u8) ARRAY_SIZE(ab8500_fg_lowbat_voltage_map) - 1;
+}
+
+/**
+ * ab8500_fg_is_low_curr() - Low or high current mode
+ * @di:                pointer to the ab8500_fg structure
+ * @curr:      the current to base or our decision on
+ *
+ * Low current mode if the current consumption is below a certain threshold
+ */
+static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr)
+{
+       /*
+        * We want to know if we're in low current mode
+        */
+       if (curr > -di->bat->fg_params->high_curr_threshold)
+               return true;
+       else
+               return false;
+}
+
+/**
+ * ab8500_fg_add_cap_sample() - Add capacity to average filter
+ * @di:                pointer to the ab8500_fg structure
+ * @sample:    the capacity in mAh to add to the filter
+ *
+ * A capacity is added to the filter and a new mean capacity is calculated and
+ * returned
+ */
+static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
+{
+       struct timespec ts;
+       struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+       getnstimeofday(&ts);
+
+       do {
+               avg->sum += sample - avg->samples[avg->pos];
+               avg->samples[avg->pos] = sample;
+               avg->time_stamps[avg->pos] = ts.tv_sec;
+               avg->pos++;
+
+               if (avg->pos == NBR_AVG_SAMPLES)
+                       avg->pos = 0;
+
+               if (avg->nbr_samples < NBR_AVG_SAMPLES)
+                       avg->nbr_samples++;
+
+               /*
+                * Check the time stamp for each sample. If too old,
+                * replace with latest sample
+                */
+       } while (ts.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
+
+       avg->avg = avg->sum / avg->nbr_samples;
+
+       return avg->avg;
+}
+
+/**
+ * ab8500_fg_clear_cap_samples() - Clear average filter
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * The capacity filter is is reset to zero.
+ */
+static void ab8500_fg_clear_cap_samples(struct ab8500_fg *di)
+{
+       int i;
+       struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+       avg->pos = 0;
+       avg->nbr_samples = 0;
+       avg->sum = 0;
+       avg->avg = 0;
+
+       for (i = 0; i < NBR_AVG_SAMPLES; i++) {
+               avg->samples[i] = 0;
+               avg->time_stamps[i] = 0;
+       }
+}
+
+/**
+ * ab8500_fg_fill_cap_sample() - Fill average filter
+ * @di:                pointer to the ab8500_fg structure
+ * @sample:    the capacity in mAh to fill the filter with
+ *
+ * The capacity filter is filled with a capacity in mAh
+ */
+static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
+{
+       int i;
+       struct timespec ts;
+       struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+       getnstimeofday(&ts);
+
+       for (i = 0; i < NBR_AVG_SAMPLES; i++) {
+               avg->samples[i] = sample;
+               avg->time_stamps[i] = ts.tv_sec;
+       }
+
+       avg->pos = 0;
+       avg->nbr_samples = NBR_AVG_SAMPLES;
+       avg->sum = sample * NBR_AVG_SAMPLES;
+       avg->avg = sample;
+}
+
+/**
+ * ab8500_fg_coulomb_counter() - enable coulomb counter
+ * @di:                pointer to the ab8500_fg structure
+ * @enable:    enable/disable
+ *
+ * Enable/Disable coulomb counter.
+ * On failure returns negative value.
+ */
+static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
+{
+       int ret = 0;
+       mutex_lock(&di->cc_lock);
+       if (enable) {
+               /* To be able to reprogram the number of samples, we have to
+                * first stop the CC and then enable it again */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG, 0x00);
+               if (ret)
+                       goto cc_err;
+
+               /* Program the samples */
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+                       di->fg_samples);
+               if (ret)
+                       goto cc_err;
+
+               /* Start the CC */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG,
+                       (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+               if (ret)
+                       goto cc_err;
+
+               di->flags.fg_enabled = true;
+       } else {
+               /* Clear any pending read requests */
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
+               if (ret)
+                       goto cc_err;
+
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL, 0);
+               if (ret)
+                       goto cc_err;
+
+               /* Stop the CC */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG, 0);
+               if (ret)
+                       goto cc_err;
+
+               di->flags.fg_enabled = false;
+
+       }
+       dev_dbg(di->dev, " CC enabled: %d Samples: %d\n",
+               enable, di->fg_samples);
+
+       mutex_unlock(&di->cc_lock);
+
+       return ret;
+cc_err:
+       dev_err(di->dev, "%s Enabling coulomb counter failed\n", __func__);
+       mutex_unlock(&di->cc_lock);
+       return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr_start() - start battery instantaneous current
+ * @di:         pointer to the ab8500_fg structure
+ *
+ * Returns 0 or error code
+ * Note: This is part "one" and has to be called before
+ * ab8500_fg_inst_curr_finalize()
+ */
+ int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
+{
+       u8 reg_val;
+       int ret;
+
+       mutex_lock(&di->cc_lock);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+               AB8500_RTC_CC_CONF_REG, &reg_val);
+       if (ret < 0)
+               goto fail;
+
+       if (!(reg_val & CC_PWR_UP_ENA)) {
+               dev_dbg(di->dev, "%s Enable FG\n", __func__);
+               di->turn_off_fg = true;
+
+               /* Program the samples */
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+                       SEC_TO_SAMPLE(10));
+               if (ret)
+                       goto fail;
+
+               /* Start the CC */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG,
+                       (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+               if (ret)
+                       goto fail;
+       } else {
+               di->turn_off_fg = false;
+       }
+
+       /* Return and WFI */
+       INIT_COMPLETION(di->ab8500_fg_complete);
+       enable_irq(di->irq);
+
+       /* Note: cc_lock is still locked */
+       return 0;
+fail:
+       mutex_unlock(&di->cc_lock);
+       return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr_done() - check if fg conversion is done
+ * @di:         pointer to the ab8500_fg structure
+ *
+ * Returns 1 if conversion done, 0 if still waiting
+ */
+int ab8500_fg_inst_curr_done(struct ab8500_fg *di)
+{
+       return completion_done(&di->ab8500_fg_complete);
+}
+
+/**
+ * ab8500_fg_inst_curr_finalize() - battery instantaneous current
+ * @di:         pointer to the ab8500_fg structure
+ * @res:       battery instantenous current(on success)
+ *
+ * Returns 0 or an error code
+ * Note: This is part "two" and has to be called at earliest 250 ms
+ * after ab8500_fg_inst_curr_start()
+ */
+int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
+{
+       u8 low, high;
+       int val;
+       int ret;
+       int timeout;
+
+       if (!completion_done(&di->ab8500_fg_complete)) {
+               timeout = wait_for_completion_timeout(&di->ab8500_fg_complete,
+                       INS_CURR_TIMEOUT);
+               dev_dbg(di->dev, "Finalize time: %d ms\n",
+                       ((INS_CURR_TIMEOUT - timeout) * 1000) / HZ);
+               if (!timeout) {
+                       ret = -ETIME;
+                       disable_irq(di->irq);
+                       dev_err(di->dev, "completion timed out [%d]\n",
+                               __LINE__);
+                       goto fail;
+               }
+       }
+
+       disable_irq(di->irq);
+
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       READ_REQ, READ_REQ);
+
+       /* 100uS between read request and read is needed */
+       usleep_range(100, 100);
+
+       /* Read CC Sample conversion value Low and high */
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_SMPL_CNVL_REG,  &low);
+       if (ret < 0)
+               goto fail;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_SMPL_CNVH_REG,  &high);
+       if (ret < 0)
+               goto fail;
+
+       /*
+        * negative value for Discharging
+        * convert 2's compliment into decimal
+        */
+       if (high & 0x10)
+               val = (low | (high << 8) | 0xFFFFE000);
+       else
+               val = (low | (high << 8));
+
+       /*
+        * Convert to unit value in mA
+        * Full scale input voltage is
+        * 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
+        * Given a 250ms conversion cycle time the LSB corresponds
+        * to 112.9 nAh. Convert to current by dividing by the conversion
+        * time in hours (250ms = 1 / (3600 * 4)h)
+        * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+        */
+       val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) /
+               (1000 * di->bat->fg_res);
+
+       if (di->turn_off_fg) {
+               dev_dbg(di->dev, "%s Disable FG\n", __func__);
+
+               /* Clear any pending read requests */
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
+               if (ret)
+                       goto fail;
+
+               /* Stop the CC */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG, 0);
+               if (ret)
+                       goto fail;
+       }
+       mutex_unlock(&di->cc_lock);
+       (*res) = val;
+
+       return 0;
+fail:
+       mutex_unlock(&di->cc_lock);
+       return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr_blocking() - battery instantaneous current
+ * @di:         pointer to the ab8500_fg structure
+ * @res:       battery instantenous current(on success)
+ *
+ * Returns 0 else error code
+ */
+int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
+{
+       int ret;
+       int res = 0;
+
+       ret = ab8500_fg_inst_curr_start(di);
+       if (ret) {
+               dev_err(di->dev, "Failed to initialize fg_inst\n");
+               return 0;
+       }
+
+       ret = ab8500_fg_inst_curr_finalize(di, &res);
+       if (ret) {
+               dev_err(di->dev, "Failed to finalize fg_inst\n");
+               return 0;
+       }
+
+       return res;
+}
+
+/**
+ * ab8500_fg_acc_cur_work() - average battery current
+ * @work:      pointer to the work_struct structure
+ *
+ * Updated the average battery current obtained from the
+ * coulomb counter.
+ */
+static void ab8500_fg_acc_cur_work(struct work_struct *work)
+{
+       int val;
+       int ret;
+       u8 low, med, high;
+
+       struct ab8500_fg *di = container_of(work,
+               struct ab8500_fg, fg_acc_cur_work);
+
+       mutex_lock(&di->cc_lock);
+       ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_CTRL, RD_NCONV_ACCU_REQ);
+       if (ret)
+               goto exit;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_LOW,  &low);
+       if (ret < 0)
+               goto exit;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_MED,  &med);
+       if (ret < 0)
+               goto exit;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_HIGH, &high);
+       if (ret < 0)
+               goto exit;
+
+       /* Check for sign bit in case of negative value, 2's compliment */
+       if (high & 0x10)
+               val = (low | (med << 8) | (high << 16) | 0xFFE00000);
+       else
+               val = (low | (med << 8) | (high << 16));
+
+       /*
+        * Convert to uAh
+        * Given a 250ms conversion cycle time the LSB corresponds
+        * to 112.9 nAh.
+        * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+        */
+       di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10) /
+               (100 * di->bat->fg_res);
+
+       /*
+        * Convert to unit value in mA
+        * Full scale input voltage is
+        * 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
+        * Given a 250ms conversion cycle time the LSB corresponds
+        * to 112.9 nAh. Convert to current by dividing by the conversion
+        * time in hours (= samples / (3600 * 4)h)
+        * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+        */
+       di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) /
+               (1000 * di->bat->fg_res * (di->fg_samples / 4));
+
+       di->flags.conv_done = true;
+
+       mutex_unlock(&di->cc_lock);
+
+       queue_work(di->fg_wq, &di->fg_work);
+
+       return;
+exit:
+       dev_err(di->dev,
+               "Failed to read or write gas gauge registers\n");
+       mutex_unlock(&di->cc_lock);
+       queue_work(di->fg_wq, &di->fg_work);
+}
+
+/**
+ * ab8500_fg_bat_voltage() - get battery voltage
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Returns battery voltage(on success) else error code
+ */
+static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
+{
+       int vbat;
+       static int prev;
+
+       vbat = ab8500_gpadc_convert(di->gpadc, MAIN_BAT_V);
+       if (vbat < 0) {
+               dev_err(di->dev,
+                       "%s gpadc conversion failed, using previous value\n",
+                       __func__);
+               return prev;
+       }
+
+       prev = vbat;
+       return vbat;
+}
+
+/**
+ * ab8500_fg_volt_to_capacity() - Voltage based capacity
+ * @di:                pointer to the ab8500_fg structure
+ * @voltage:   The voltage to convert to a capacity
+ *
+ * Returns battery capacity in per mille based on voltage
+ */
+static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
+{
+       int i, tbl_size;
+       struct abx500_v_to_cap *tbl;
+       int cap = 0;
+
+       tbl = di->bat->bat_type[di->bat->batt_id].v_to_cap_tbl,
+       tbl_size = di->bat->bat_type[di->bat->batt_id].n_v_cap_tbl_elements;
+
+       for (i = 0; i < tbl_size; ++i) {
+               if (voltage > tbl[i].voltage)
+                       break;
+       }
+
+       if ((i > 0) && (i < tbl_size)) {
+               cap = interpolate(voltage,
+                       tbl[i].voltage,
+                       tbl[i].capacity * 10,
+                       tbl[i-1].voltage,
+                       tbl[i-1].capacity * 10);
+       } else if (i == 0) {
+               cap = 1000;
+       } else {
+               cap = 0;
+       }
+
+       dev_dbg(di->dev, "%s Vbat: %d, Cap: %d per mille",
+               __func__, voltage, cap);
+
+       return cap;
+}
+
+/**
+ * ab8500_fg_uncomp_volt_to_capacity() - Uncompensated voltage based capacity
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is not compensated
+ * for the voltage drop due to the load
+ */
+static int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di)
+{
+       di->vbat = ab8500_fg_bat_voltage(di);
+       return ab8500_fg_volt_to_capacity(di, di->vbat);
+}
+
+/**
+ * ab8500_fg_battery_resistance() - Returns the battery inner resistance
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Returns battery inner resistance added with the fuel gauge resistor value
+ * to get the total resistance in the whole link from gnd to bat+ node.
+ */
+static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
+{
+       int i, tbl_size;
+       struct batres_vs_temp *tbl;
+       int resist = 0;
+
+       tbl = di->bat->bat_type[di->bat->batt_id].batres_tbl;
+       tbl_size = di->bat->bat_type[di->bat->batt_id].n_batres_tbl_elements;
+
+       for (i = 0; i < tbl_size; ++i) {
+               if (di->bat_temp / 10 > tbl[i].temp)
+                       break;
+       }
+
+       if ((i > 0) && (i < tbl_size)) {
+               resist = interpolate(di->bat_temp / 10,
+                       tbl[i].temp,
+                       tbl[i].resist,
+                       tbl[i-1].temp,
+                       tbl[i-1].resist);
+       } else if (i == 0) {
+               resist = tbl[0].resist;
+       } else {
+               resist = tbl[tbl_size - 1].resist;
+       }
+
+       dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d"
+           " fg resistance %d, total: %d (mOhm)\n",
+               __func__, di->bat_temp, resist, di->bat->fg_res / 10,
+               (di->bat->fg_res / 10) + resist);
+
+       /* fg_res variable is in 0.1mOhm */
+       resist += di->bat->fg_res / 10;
+
+       return resist;
+}
+
+/**
+ * ab8500_fg_load_comp_volt_to_capacity() - Load compensated voltage based capacity
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is load compensated
+ * for the voltage drop
+ */
+static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
+{
+       int vbat_comp, res;
+       int i = 0;
+       int vbat = 0;
+
+       ab8500_fg_inst_curr_start(di);
+
+       do {
+               vbat += ab8500_fg_bat_voltage(di);
+               i++;
+               msleep(5);
+       } while (!ab8500_fg_inst_curr_done(di));
+
+       ab8500_fg_inst_curr_finalize(di, &di->inst_curr);
+
+       di->vbat = vbat / i;
+       res = ab8500_fg_battery_resistance(di);
+
+       /* Use Ohms law to get the load compensated voltage */
+       vbat_comp = di->vbat - (di->inst_curr * res) / 1000;
+
+       dev_dbg(di->dev, "%s Measured Vbat: %dmV,Compensated Vbat %dmV, "
+               "R: %dmOhm, Current: %dmA Vbat Samples: %d\n",
+               __func__, di->vbat, vbat_comp, res, di->inst_curr, i);
+
+       return ab8500_fg_volt_to_capacity(di, vbat_comp);
+}
+
+/**
+ * ab8500_fg_convert_mah_to_permille() - Capacity in mAh to permille
+ * @di:                pointer to the ab8500_fg structure
+ * @cap_mah:   capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in permille
+ */
+static int ab8500_fg_convert_mah_to_permille(struct ab8500_fg *di, int cap_mah)
+{
+       return (cap_mah * 1000) / di->bat_cap.max_mah_design;
+}
+
+/**
+ * ab8500_fg_convert_permille_to_mah() - Capacity in permille to mAh
+ * @di:                pointer to the ab8500_fg structure
+ * @cap_pm:    capacity in permille
+ *
+ * Converts capacity in permille to capacity in mAh
+ */
+static int ab8500_fg_convert_permille_to_mah(struct ab8500_fg *di, int cap_pm)
+{
+       return cap_pm * di->bat_cap.max_mah_design / 1000;
+}
+
+/**
+ * ab8500_fg_convert_mah_to_uwh() - Capacity in mAh to uWh
+ * @di:                pointer to the ab8500_fg structure
+ * @cap_mah:   capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in uWh
+ */
+static int ab8500_fg_convert_mah_to_uwh(struct ab8500_fg *di, int cap_mah)
+{
+       u64 div_res;
+       u32 div_rem;
+
+       div_res = ((u64) cap_mah) * ((u64) di->vbat_nom);
+       div_rem = do_div(div_res, 1000);
+
+       /* Make sure to round upwards if necessary */
+       if (div_rem >= 1000 / 2)
+               div_res++;
+
+       return (int) div_res;
+}
+
+/**
+ * ab8500_fg_calc_cap_charging() - Calculate remaining capacity while charging
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. The filter is filled with this capacity
+ */
+static int ab8500_fg_calc_cap_charging(struct ab8500_fg *di)
+{
+       dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+               __func__,
+               di->bat_cap.mah,
+               di->accu_charge);
+
+       /* Capacity should not be less than 0 */
+       if (di->bat_cap.mah + di->accu_charge > 0)
+               di->bat_cap.mah += di->accu_charge;
+       else
+               di->bat_cap.mah = 0;
+       /*
+        * We force capacity to 100% once when the algorithm
+        * reports that it's full.
+        */
+       if (di->bat_cap.mah >= di->bat_cap.max_mah_design ||
+               di->flags.force_full) {
+               di->bat_cap.mah = di->bat_cap.max_mah_design;
+       }
+
+       ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+       di->bat_cap.permille =
+               ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+       /* We need to update battery voltage and inst current when charging */
+       di->vbat = ab8500_fg_bat_voltage(di);
+       di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+       return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_calc_cap_discharge_voltage() - Capacity in discharge with voltage
+ * @di:                pointer to the ab8500_fg structure
+ * @comp:      if voltage should be load compensated before capacity calc
+ *
+ * Return the capacity in mAh based on the battery voltage. The voltage can
+ * either be load compensated or not. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab8500_fg_calc_cap_discharge_voltage(struct ab8500_fg *di, bool comp)
+{
+       int permille, mah;
+
+       if (comp)
+               permille = ab8500_fg_load_comp_volt_to_capacity(di);
+       else
+               permille = ab8500_fg_uncomp_volt_to_capacity(di);
+
+       mah = ab8500_fg_convert_permille_to_mah(di, permille);
+
+       di->bat_cap.mah = ab8500_fg_add_cap_sample(di, mah);
+       di->bat_cap.permille =
+               ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+       return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_calc_cap_discharge_fg() - Capacity in discharge with FG
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab8500_fg_calc_cap_discharge_fg(struct ab8500_fg *di)
+{
+       int permille_volt, permille;
+
+       dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+               __func__,
+               di->bat_cap.mah,
+               di->accu_charge);
+
+       /* Capacity should not be less than 0 */
+       if (di->bat_cap.mah + di->accu_charge > 0)
+               di->bat_cap.mah += di->accu_charge;
+       else
+               di->bat_cap.mah = 0;
+
+       if (di->bat_cap.mah >= di->bat_cap.max_mah_design)
+               di->bat_cap.mah = di->bat_cap.max_mah_design;
+
+       /*
+        * Check against voltage based capacity. It can not be lower
+        * than what the uncompensated voltage says
+        */
+       permille = ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+       permille_volt = ab8500_fg_uncomp_volt_to_capacity(di);
+
+       if (permille < permille_volt) {
+               di->bat_cap.permille = permille_volt;
+               di->bat_cap.mah = ab8500_fg_convert_permille_to_mah(di,
+                       di->bat_cap.permille);
+
+               dev_dbg(di->dev, "%s voltage based: perm %d perm_volt %d\n",
+                       __func__,
+                       permille,
+                       permille_volt);
+
+               ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+       } else {
+               ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+               di->bat_cap.permille =
+                       ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+       }
+
+       return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_capacity_level() - Get the battery capacity level
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Get the battery capacity level based on the capacity in percent
+ */
+static int ab8500_fg_capacity_level(struct ab8500_fg *di)
+{
+       int ret, percent;
+
+       percent = di->bat_cap.permille / 10;
+
+       if (percent <= di->bat->cap_levels->critical ||
+               di->flags.low_bat)
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+       else if (percent <= di->bat->cap_levels->low)
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+       else if (percent <= di->bat->cap_levels->normal)
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+       else if (percent <= di->bat->cap_levels->high)
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+       else
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_check_capacity_limits() - Check if capacity has changed
+ * @di:                pointer to the ab8500_fg structure
+ * @init:      capacity is allowed to go up in init mode
+ *
+ * Check if capacity or capacity limit has changed and notify the system
+ * about it using the power_supply framework
+ */
+static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
+{
+       bool changed = false;
+
+       di->bat_cap.level = ab8500_fg_capacity_level(di);
+
+       if (di->bat_cap.level != di->bat_cap.prev_level) {
+               /*
+                * We do not allow reported capacity level to go up
+                * unless we're charging or if we're in init
+                */
+               if (!(!di->flags.charging && di->bat_cap.level >
+                       di->bat_cap.prev_level) || init) {
+                       dev_dbg(di->dev, "level changed from %d to %d\n",
+                               di->bat_cap.prev_level,
+                               di->bat_cap.level);
+                       di->bat_cap.prev_level = di->bat_cap.level;
+                       changed = true;
+               } else {
+                       dev_dbg(di->dev, "level not allowed to go up "
+                               "since no charger is connected: %d to %d\n",
+                               di->bat_cap.prev_level,
+                               di->bat_cap.level);
+               }
+       }
+
+       /*
+        * If we have received the LOW_BAT IRQ, set capacity to 0 to initiate
+        * shutdown
+        */
+       if (di->flags.low_bat) {
+               dev_dbg(di->dev, "Battery low, set capacity to 0\n");
+               di->bat_cap.prev_percent = 0;
+               di->bat_cap.permille = 0;
+               di->bat_cap.prev_mah = 0;
+               di->bat_cap.mah = 0;
+               changed = true;
+       } else if (di->flags.fully_charged) {
+               /*
+                * We report 100% if algorithm reported fully charged
+                * unless capacity drops too much
+                */
+               if (di->flags.force_full) {
+                       di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+                       di->bat_cap.prev_mah = di->bat_cap.mah;
+               } else if (!di->flags.force_full &&
+                       di->bat_cap.prev_percent !=
+                       (di->bat_cap.permille) / 10 &&
+                       (di->bat_cap.permille / 10) <
+                       di->bat->fg_params->maint_thres) {
+                       dev_dbg(di->dev,
+                               "battery reported full "
+                               "but capacity dropping: %d\n",
+                               di->bat_cap.permille / 10);
+                       di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+                       di->bat_cap.prev_mah = di->bat_cap.mah;
+
+                       changed = true;
+               }
+       } else if (di->bat_cap.prev_percent != di->bat_cap.permille / 10) {
+               if (di->bat_cap.permille / 10 == 0) {
+                       /*
+                        * We will not report 0% unless we've got
+                        * the LOW_BAT IRQ, no matter what the FG
+                        * algorithm says.
+                        */
+                       di->bat_cap.prev_percent = 1;
+                       di->bat_cap.permille = 1;
+                       di->bat_cap.prev_mah = 1;
+                       di->bat_cap.mah = 1;
+
+                       changed = true;
+               } else if (!(!di->flags.charging &&
+                       (di->bat_cap.permille / 10) >
+                       di->bat_cap.prev_percent) || init) {
+                       /*
+                        * We do not allow reported capacity to go up
+                        * unless we're charging or if we're in init
+                        */
+                       dev_dbg(di->dev,
+                               "capacity changed from %d to %d (%d)\n",
+                               di->bat_cap.prev_percent,
+                               di->bat_cap.permille / 10,
+                               di->bat_cap.permille);
+                       di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+                       di->bat_cap.prev_mah = di->bat_cap.mah;
+
+                       changed = true;
+               } else {
+                       dev_dbg(di->dev, "capacity not allowed to go up since "
+                               "no charger is connected: %d to %d (%d)\n",
+                               di->bat_cap.prev_percent,
+                               di->bat_cap.permille / 10,
+                               di->bat_cap.permille);
+               }
+       }
+
+       if (changed) {
+               power_supply_changed(&di->fg_psy);
+               if (di->flags.fully_charged && di->flags.force_full) {
+                       dev_dbg(di->dev, "Battery full, notifying.\n");
+                       di->flags.force_full = false;
+                       sysfs_notify(&di->fg_kobject, NULL, "charge_full");
+               }
+               sysfs_notify(&di->fg_kobject, NULL, "charge_now");
+       }
+}
+
+static void ab8500_fg_charge_state_to(struct ab8500_fg *di,
+       enum ab8500_fg_charge_state new_state)
+{
+       dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n",
+               di->charge_state,
+               charge_state[di->charge_state],
+               new_state,
+               charge_state[new_state]);
+
+       di->charge_state = new_state;
+}
+
+static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
+       enum ab8500_fg_discharge_state new_state)
+{
+       dev_dbg(di->dev, "Disharge state from %d [%s] to %d [%s]\n",
+               di->discharge_state,
+               discharge_state[di->discharge_state],
+               new_state,
+               discharge_state[new_state]);
+
+       di->discharge_state = new_state;
+}
+
+/**
+ * ab8500_fg_algorithm_charging() - FG algorithm for when charging
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're charging
+ */
+static void ab8500_fg_algorithm_charging(struct ab8500_fg *di)
+{
+       /*
+        * If we change to discharge mode
+        * we should start with recovery
+        */
+       if (di->discharge_state != AB8500_FG_DISCHARGE_INIT_RECOVERY)
+               ab8500_fg_discharge_state_to(di,
+                       AB8500_FG_DISCHARGE_INIT_RECOVERY);
+
+       switch (di->charge_state) {
+       case AB8500_FG_CHARGE_INIT:
+               di->fg_samples = SEC_TO_SAMPLE(
+                       di->bat->fg_params->accu_charging);
+
+               ab8500_fg_coulomb_counter(di, true);
+               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_READOUT);
+
+               break;
+
+       case AB8500_FG_CHARGE_READOUT:
+               /*
+                * Read the FG and calculate the new capacity
+                */
+               mutex_lock(&di->cc_lock);
+               if (!di->flags.conv_done) {
+                       /* Wasn't the CC IRQ that got us here */
+                       mutex_unlock(&di->cc_lock);
+                       dev_dbg(di->dev, "%s CC conv not done\n",
+                               __func__);
+
+                       break;
+               }
+               di->flags.conv_done = false;
+               mutex_unlock(&di->cc_lock);
+
+               ab8500_fg_calc_cap_charging(di);
+
+               break;
+
+       default:
+               break;
+       }
+
+       /* Check capacity limits */
+       ab8500_fg_check_capacity_limits(di, false);
+}
+
+static void force_capacity(struct ab8500_fg *di)
+{
+       int cap;
+
+       ab8500_fg_clear_cap_samples(di);
+       cap = di->bat_cap.user_mah;
+       if (cap > di->bat_cap.max_mah_design) {
+               dev_dbg(di->dev, "Remaining cap %d can't be bigger than total"
+                       " %d\n", cap, di->bat_cap.max_mah_design);
+               cap = di->bat_cap.max_mah_design;
+       }
+       ab8500_fg_fill_cap_sample(di, di->bat_cap.user_mah);
+       di->bat_cap.permille = ab8500_fg_convert_mah_to_permille(di, cap);
+       di->bat_cap.mah = cap;
+       ab8500_fg_check_capacity_limits(di, true);
+}
+
+static bool check_sysfs_capacity(struct ab8500_fg *di)
+{
+       int cap, lower, upper;
+       int cap_permille;
+
+       cap = di->bat_cap.user_mah;
+
+       cap_permille = ab8500_fg_convert_mah_to_permille(di,
+               di->bat_cap.user_mah);
+
+       lower = di->bat_cap.permille - di->bat->fg_params->user_cap_limit * 10;
+       upper = di->bat_cap.permille + di->bat->fg_params->user_cap_limit * 10;
+
+       if (lower < 0)
+               lower = 0;
+       /* 1000 is permille, -> 100 percent */
+       if (upper > 1000)
+               upper = 1000;
+
+       dev_dbg(di->dev, "Capacity limits:"
+               " (Lower: %d User: %d Upper: %d) [user: %d, was: %d]\n",
+               lower, cap_permille, upper, cap, di->bat_cap.mah);
+
+       /* If within limits, use the saved capacity and exit estimation...*/
+       if (cap_permille > lower && cap_permille < upper) {
+               dev_dbg(di->dev, "OK! Using users cap %d uAh now\n", cap);
+               force_capacity(di);
+               return true;
+       }
+       dev_dbg(di->dev, "Capacity from user out of limits, ignoring");
+       return false;
+}
+
+/**
+ * ab8500_fg_algorithm_discharging() - FG algorithm for when discharging
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're discharging
+ */
+static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
+{
+       int sleep_time;
+
+       /* If we change to charge mode we should start with init */
+       if (di->charge_state != AB8500_FG_CHARGE_INIT)
+               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+
+       switch (di->discharge_state) {
+       case AB8500_FG_DISCHARGE_INIT:
+               /* We use the FG IRQ to work on */
+               di->init_cnt = 0;
+               di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
+               ab8500_fg_coulomb_counter(di, true);
+               ab8500_fg_discharge_state_to(di,
+                       AB8500_FG_DISCHARGE_INITMEASURING);
+
+               /* Intentional fallthrough */
+       case AB8500_FG_DISCHARGE_INITMEASURING:
+               /*
+                * Discard a number of samples during startup.
+                * After that, use compensated voltage for a few
+                * samples to get an initial capacity.
+                * Then go to READOUT
+                */
+               sleep_time = di->bat->fg_params->init_timer;
+
+               /* Discard the first [x] seconds */
+               if (di->init_cnt >
+                       di->bat->fg_params->init_discard_time) {
+                       ab8500_fg_calc_cap_discharge_voltage(di, true);
+
+                       ab8500_fg_check_capacity_limits(di, true);
+               }
+
+               di->init_cnt += sleep_time;
+               if (di->init_cnt > di->bat->fg_params->init_total_time)
+                       ab8500_fg_discharge_state_to(di,
+                               AB8500_FG_DISCHARGE_READOUT_INIT);
+
+               break;
+
+       case AB8500_FG_DISCHARGE_INIT_RECOVERY:
+               di->recovery_cnt = 0;
+               di->recovery_needed = true;
+               ab8500_fg_discharge_state_to(di,
+                       AB8500_FG_DISCHARGE_RECOVERY);
+
+               /* Intentional fallthrough */
+
+       case AB8500_FG_DISCHARGE_RECOVERY:
+               sleep_time = di->bat->fg_params->recovery_sleep_timer;
+
+               /*
+                * We should check the power consumption
+                * If low, go to READOUT (after x min) or
+                * RECOVERY_SLEEP if time left.
+                * If high, go to READOUT
+                */
+               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+               if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
+                       if (di->recovery_cnt >
+                               di->bat->fg_params->recovery_total_time) {
+                               di->fg_samples = SEC_TO_SAMPLE(
+                                       di->bat->fg_params->accu_high_curr);
+                               ab8500_fg_coulomb_counter(di, true);
+                               ab8500_fg_discharge_state_to(di,
+                                       AB8500_FG_DISCHARGE_READOUT);
+                               di->recovery_needed = false;
+                       } else {
+                               queue_delayed_work(di->fg_wq,
+                                       &di->fg_periodic_work,
+                                       sleep_time * HZ);
+                       }
+                       di->recovery_cnt += sleep_time;
+               } else {
+                       di->fg_samples = SEC_TO_SAMPLE(
+                               di->bat->fg_params->accu_high_curr);
+                       ab8500_fg_coulomb_counter(di, true);
+                       ab8500_fg_discharge_state_to(di,
+                               AB8500_FG_DISCHARGE_READOUT);
+               }
+               break;
+
+       case AB8500_FG_DISCHARGE_READOUT_INIT:
+               di->fg_samples = SEC_TO_SAMPLE(
+                       di->bat->fg_params->accu_high_curr);
+               ab8500_fg_coulomb_counter(di, true);
+               ab8500_fg_discharge_state_to(di,
+                               AB8500_FG_DISCHARGE_READOUT);
+               break;
+
+       case AB8500_FG_DISCHARGE_READOUT:
+               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+               if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
+                       /* Detect mode change */
+                       if (di->high_curr_mode) {
+                               di->high_curr_mode = false;
+                               di->high_curr_cnt = 0;
+                       }
+
+                       if (di->recovery_needed) {
+                               ab8500_fg_discharge_state_to(di,
+                                       AB8500_FG_DISCHARGE_RECOVERY);
+
+                               queue_delayed_work(di->fg_wq,
+                                       &di->fg_periodic_work, 0);
+
+                               break;
+                       }
+
+                       ab8500_fg_calc_cap_discharge_voltage(di, true);
+               } else {
+                       mutex_lock(&di->cc_lock);
+                       if (!di->flags.conv_done) {
+                               /* Wasn't the CC IRQ that got us here */
+                               mutex_unlock(&di->cc_lock);
+                               dev_dbg(di->dev, "%s CC conv not done\n",
+                                       __func__);
+
+                               break;
+                       }
+                       di->flags.conv_done = false;
+                       mutex_unlock(&di->cc_lock);
+
+                       /* Detect mode change */
+                       if (!di->high_curr_mode) {
+                               di->high_curr_mode = true;
+                               di->high_curr_cnt = 0;
+                       }
+
+                       di->high_curr_cnt +=
+                               di->bat->fg_params->accu_high_curr;
+                       if (di->high_curr_cnt >
+                               di->bat->fg_params->high_curr_time)
+                               di->recovery_needed = true;
+
+                       ab8500_fg_calc_cap_discharge_fg(di);
+               }
+
+               ab8500_fg_check_capacity_limits(di, false);
+
+               break;
+
+       case AB8500_FG_DISCHARGE_WAKEUP:
+               ab8500_fg_coulomb_counter(di, true);
+               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+               ab8500_fg_calc_cap_discharge_voltage(di, true);
+
+               di->fg_samples = SEC_TO_SAMPLE(
+                       di->bat->fg_params->accu_high_curr);
+               ab8500_fg_coulomb_counter(di, true);
+               ab8500_fg_discharge_state_to(di,
+                               AB8500_FG_DISCHARGE_READOUT);
+
+               ab8500_fg_check_capacity_limits(di, false);
+
+               break;
+
+       default:
+               break;
+       }
+}
+
+/**
+ * ab8500_fg_algorithm_calibrate() - Internal columb counter offset calibration
+ * @di:                pointer to the ab8500_fg structure
+ *
+ */
+static void ab8500_fg_algorithm_calibrate(struct ab8500_fg *di)
+{
+       int ret;
+
+       switch (di->calib_state) {
+       case AB8500_FG_CALIB_INIT:
+               dev_dbg(di->dev, "Calibration ongoing...\n");
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       CC_INT_CAL_N_AVG_MASK, CC_INT_CAL_SAMPLES_8);
+               if (ret < 0)
+                       goto err;
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       CC_INTAVGOFFSET_ENA, CC_INTAVGOFFSET_ENA);
+               if (ret < 0)
+                       goto err;
+               di->calib_state = AB8500_FG_CALIB_WAIT;
+               break;
+       case AB8500_FG_CALIB_END:
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       CC_MUXOFFSET, CC_MUXOFFSET);
+               if (ret < 0)
+                       goto err;
+               di->flags.calibrate = false;
+               dev_dbg(di->dev, "Calibration done...\n");
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+               break;
+       case AB8500_FG_CALIB_WAIT:
+               dev_dbg(di->dev, "Calibration WFI\n");
+       default:
+               break;
+       }
+       return;
+err:
+       /* Something went wrong, don't calibrate then */
+       dev_err(di->dev, "failed to calibrate the CC\n");
+       di->flags.calibrate = false;
+       di->calib_state = AB8500_FG_CALIB_INIT;
+       queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+}
+
+/**
+ * ab8500_fg_algorithm() - Entry point for the FG algorithm
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Entry point for the battery capacity calculation state machine
+ */
+static void ab8500_fg_algorithm(struct ab8500_fg *di)
+{
+       if (di->flags.calibrate)
+               ab8500_fg_algorithm_calibrate(di);
+       else {
+               if (di->flags.charging)
+                       ab8500_fg_algorithm_charging(di);
+               else
+                       ab8500_fg_algorithm_discharging(di);
+       }
+
+       dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d "
+               "%d %d %d %d %d %d %d\n",
+               di->bat_cap.max_mah_design,
+               di->bat_cap.mah,
+               di->bat_cap.permille,
+               di->bat_cap.level,
+               di->bat_cap.prev_mah,
+               di->bat_cap.prev_percent,
+               di->bat_cap.prev_level,
+               di->vbat,
+               di->inst_curr,
+               di->avg_curr,
+               di->accu_charge,
+               di->flags.charging,
+               di->charge_state,
+               di->discharge_state,
+               di->high_curr_mode,
+               di->recovery_needed);
+}
+
+/**
+ * ab8500_fg_periodic_work() - Run the FG state machine periodically
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for periodic work
+ */
+static void ab8500_fg_periodic_work(struct work_struct *work)
+{
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+               fg_periodic_work.work);
+
+       if (di->init_capacity) {
+               /* A dummy read that will return 0 */
+               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+               /* Get an initial capacity calculation */
+               ab8500_fg_calc_cap_discharge_voltage(di, true);
+               ab8500_fg_check_capacity_limits(di, true);
+               di->init_capacity = false;
+
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+       } else if (di->flags.user_cap) {
+               if (check_sysfs_capacity(di)) {
+                       ab8500_fg_check_capacity_limits(di, true);
+                       if (di->flags.charging)
+                               ab8500_fg_charge_state_to(di,
+                                       AB8500_FG_CHARGE_INIT);
+                       else
+                               ab8500_fg_discharge_state_to(di,
+                                       AB8500_FG_DISCHARGE_READOUT_INIT);
+               }
+               di->flags.user_cap = false;
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+       } else
+               ab8500_fg_algorithm(di);
+
+}
+
+/**
+ * ab8500_fg_check_hw_failure_work() - Check OVV_BAT condition
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the OVV_BAT condition
+ */
+static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+               fg_check_hw_failure_work.work);
+
+       /*
+        * If we have had a battery over-voltage situation,
+        * check ovv-bit to see if it should be reset.
+        */
+       if (di->flags.bat_ovv) {
+               ret = abx500_get_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_CH_STAT_REG,
+                       &reg_value);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return;
+               }
+               if ((reg_value & BATT_OVV) != BATT_OVV) {
+                       dev_dbg(di->dev, "Battery recovered from OVV\n");
+                       di->flags.bat_ovv = false;
+                       power_supply_changed(&di->fg_psy);
+                       return;
+               }
+
+               /* Not yet recovered from ovv, reschedule this test */
+               queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
+                                  round_jiffies(HZ));
+       }
+}
+
+/**
+ * ab8500_fg_low_bat_work() - Check LOW_BAT condition
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the LOW_BAT condition
+ */
+static void ab8500_fg_low_bat_work(struct work_struct *work)
+{
+       int vbat;
+
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+               fg_low_bat_work.work);
+
+       vbat = ab8500_fg_bat_voltage(di);
+
+       /* Check if LOW_BAT still fulfilled */
+       if (vbat < di->bat->fg_params->lowbat_threshold) {
+               di->flags.low_bat = true;
+               dev_warn(di->dev, "Battery voltage still LOW\n");
+
+               /*
+                * We need to re-schedule this check to be able to detect
+                * if the voltage increases again during charging
+                */
+               queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+                       round_jiffies(LOW_BAT_CHECK_INTERVAL));
+       } else {
+               di->flags.low_bat = false;
+               dev_warn(di->dev, "Battery voltage OK again\n");
+       }
+
+       /* This is needed to dispatch LOW_BAT */
+       ab8500_fg_check_capacity_limits(di, false);
+
+       /* Set this flag to check if LOW_BAT IRQ still occurs */
+       di->flags.low_bat_delay = false;
+}
+
+/**
+ * ab8500_fg_battok_calc - calculate the bit pattern corresponding
+ * to the target voltage.
+ * @di:       pointer to the ab8500_fg structure
+ * @target    target voltage
+ *
+ * Returns bit pattern closest to the target voltage
+ * valid return values are 0-14. (0-BATT_OK_MAX_NR_INCREMENTS)
+ */
+
+static int ab8500_fg_battok_calc(struct ab8500_fg *di, int target)
+{
+       if (target > BATT_OK_MIN +
+               (BATT_OK_INCREMENT * BATT_OK_MAX_NR_INCREMENTS))
+               return BATT_OK_MAX_NR_INCREMENTS;
+       if (target < BATT_OK_MIN)
+               return 0;
+       return (target - BATT_OK_MIN) / BATT_OK_INCREMENT;
+}
+
+/**
+ * ab8500_fg_battok_init_hw_register - init battok levels
+ * @di:       pointer to the ab8500_fg structure
+ *
+ */
+
+static int ab8500_fg_battok_init_hw_register(struct ab8500_fg *di)
+{
+       int selected;
+       int sel0;
+       int sel1;
+       int cbp_sel0;
+       int cbp_sel1;
+       int ret;
+       int new_val;
+
+       sel0 = di->bat->fg_params->battok_falling_th_sel0;
+       sel1 = di->bat->fg_params->battok_raising_th_sel1;
+
+       cbp_sel0 = ab8500_fg_battok_calc(di, sel0);
+       cbp_sel1 = ab8500_fg_battok_calc(di, sel1);
+
+       selected = BATT_OK_MIN + cbp_sel0 * BATT_OK_INCREMENT;
+
+       if (selected != sel0)
+               dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n",
+                       sel0, selected, cbp_sel0);
+
+       selected = BATT_OK_MIN + cbp_sel1 * BATT_OK_INCREMENT;
+
+       if (selected != sel1)
+               dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n",
+                       sel1, selected, cbp_sel1);
+
+       new_val = cbp_sel0 | (cbp_sel1 << 4);
+
+       dev_dbg(di->dev, "using: %x %d %d\n", new_val, cbp_sel0, cbp_sel1);
+       ret = abx500_set_register_interruptible(di->dev, AB8500_SYS_CTRL2_BLOCK,
+               AB8500_BATT_OK_REG, new_val);
+       return ret;
+}
+
+/**
+ * ab8500_fg_instant_work() - Run the FG state machine instantly
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for instant work
+ */
+static void ab8500_fg_instant_work(struct work_struct *work)
+{
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg, fg_work);
+
+       ab8500_fg_algorithm(di);
+}
+
+/**
+ * ab8500_fg_cc_data_end_handler() - isr to get battery avg current.
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+       complete(&di->ab8500_fg_complete);
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_int_calib_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+       di->calib_state = AB8500_FG_CALIB_END;
+       queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+
+       queue_work(di->fg_wq, &di->fg_acc_cur_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_batt_ovv_handler() - Battery OVV occured
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+
+       dev_dbg(di->dev, "Battery OVV\n");
+       di->flags.bat_ovv = true;
+       power_supply_changed(&di->fg_psy);
+
+       /* Schedule a new HW failure check */
+       queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_lowbatf_handler() - Battery voltage is below LOW threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+
+       if (!di->flags.low_bat_delay) {
+               dev_warn(di->dev, "Battery voltage is below LOW threshold\n");
+               di->flags.low_bat_delay = true;
+               /*
+                * Start a timer to check LOW_BAT again after some time
+                * This is done to avoid shutdown on single voltage dips
+                */
+               queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+                       round_jiffies(LOW_BAT_CHECK_INTERVAL));
+       }
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_get_property() - get the fg properties
+ * @psy:       pointer to the power_supply structure
+ * @psp:       pointer to the power_supply_property structure
+ * @val:       pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the
+ * fg properties by reading the sysfs files.
+ * voltage_now:                battery voltage
+ * current_now:                battery instant current
+ * current_avg:                battery average current
+ * charge_full_design: capacity where battery is considered full
+ * charge_now:         battery capacity in nAh
+ * capacity:           capacity in percent
+ * capacity_level:     capacity level
+ *
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_fg_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct ab8500_fg *di;
+
+       di = to_ab8500_fg_device_info(psy);
+
+       /*
+        * If battery is identified as unknown and charging of unknown
+        * batteries is disabled, we always report 100% capacity and
+        * capacity level UNKNOWN, since we can't calculate
+        * remaining capacity
+        */
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (di->flags.bat_ovv)
+                       val->intval = BATT_OVV_VALUE * 1000;
+               else
+                       val->intval = di->vbat * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = di->inst_curr * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               val->intval = di->avg_curr * 1000;
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+               val->intval = ab8500_fg_convert_mah_to_uwh(di,
+                               di->bat_cap.max_mah_design);
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_FULL:
+               val->intval = ab8500_fg_convert_mah_to_uwh(di,
+                               di->bat_cap.max_mah);
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_NOW:
+               if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+                               di->flags.batt_id_received)
+                       val->intval = ab8500_fg_convert_mah_to_uwh(di,
+                                       di->bat_cap.max_mah);
+               else
+                       val->intval = ab8500_fg_convert_mah_to_uwh(di,
+                                       di->bat_cap.prev_mah);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval = di->bat_cap.max_mah_design;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               val->intval = di->bat_cap.max_mah;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+                               di->flags.batt_id_received)
+                       val->intval = di->bat_cap.max_mah;
+               else
+                       val->intval = di->bat_cap.prev_mah;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+                               di->flags.batt_id_received)
+                       val->intval = 100;
+               else
+                       val->intval = di->bat_cap.prev_percent;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+               if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+                               di->flags.batt_id_received)
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+               else
+                       val->intval = di->bat_cap.prev_level;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
+{
+       struct power_supply *psy;
+       struct power_supply *ext;
+       struct ab8500_fg *di;
+       union power_supply_propval ret;
+       int i, j;
+       bool psy_found = false;
+
+       psy = (struct power_supply *)data;
+       ext = dev_get_drvdata(dev);
+       di = to_ab8500_fg_device_info(psy);
+
+       /*
+        * For all psy where the name of your driver
+        * appears in any supplied_to
+        */
+       for (i = 0; i < ext->num_supplicants; i++) {
+               if (!strcmp(ext->supplied_to[i], psy->name))
+                       psy_found = true;
+       }
+
+       if (!psy_found)
+               return 0;
+
+       /* Go through all properties for the psy */
+       for (j = 0; j < ext->num_properties; j++) {
+               enum power_supply_property prop;
+               prop = ext->properties[j];
+
+               if (ext->get_property(ext, prop, &ret))
+                       continue;
+
+               switch (prop) {
+               case POWER_SUPPLY_PROP_STATUS:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               switch (ret.intval) {
+                               case POWER_SUPPLY_STATUS_UNKNOWN:
+                               case POWER_SUPPLY_STATUS_DISCHARGING:
+                               case POWER_SUPPLY_STATUS_NOT_CHARGING:
+                                       if (!di->flags.charging)
+                                               break;
+                                       di->flags.charging = false;
+                                       di->flags.fully_charged = false;
+                                       queue_work(di->fg_wq, &di->fg_work);
+                                       break;
+                               case POWER_SUPPLY_STATUS_FULL:
+                                       if (di->flags.fully_charged)
+                                               break;
+                                       di->flags.fully_charged = true;
+                                       di->flags.force_full = true;
+                                       /* Save current capacity as maximum */
+                                       di->bat_cap.max_mah = di->bat_cap.mah;
+                                       queue_work(di->fg_wq, &di->fg_work);
+                                       break;
+                               case POWER_SUPPLY_STATUS_CHARGING:
+                                       if (di->flags.charging)
+                                               break;
+                                       di->flags.charging = true;
+                                       di->flags.fully_charged = false;
+                                       queue_work(di->fg_wq, &di->fg_work);
+                                       break;
+                               };
+                       default:
+                               break;
+                       };
+                       break;
+               case POWER_SUPPLY_PROP_TECHNOLOGY:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               if (!di->flags.batt_id_received) {
+                                       const struct abx500_battery_type *b;
+
+                                       b = &(di->bat->bat_type[di->bat->batt_id]);
+
+                                       di->flags.batt_id_received = true;
+
+                                       di->bat_cap.max_mah_design =
+                                               MILLI_TO_MICRO *
+                                               b->charge_full_design;
+
+                                       di->bat_cap.max_mah =
+                                               di->bat_cap.max_mah_design;
+
+                                       di->vbat_nom = b->nominal_voltage;
+                               }
+
+                               if (ret.intval)
+                                       di->flags.batt_unknown = false;
+                               else
+                                       di->flags.batt_unknown = true;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case POWER_SUPPLY_PROP_TEMP:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                           if (di->flags.batt_id_received)
+                               di->bat_temp = ret.intval;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+/**
+ * ab8500_fg_init_hw_registers() - Set up FG related registers
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Set up battery OVV, low battery voltage registers
+ */
+static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
+{
+       int ret;
+
+       /* Set VBAT OVV threshold */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_CHARGER,
+               AB8500_BATT_OVV,
+               BATT_OVV_TH_4P75,
+               BATT_OVV_TH_4P75);
+       if (ret) {
+               dev_err(di->dev, "failed to set BATT_OVV\n");
+               goto out;
+       }
+
+       /* Enable VBAT OVV detection */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_CHARGER,
+               AB8500_BATT_OVV,
+               BATT_OVV_ENA,
+               BATT_OVV_ENA);
+       if (ret) {
+               dev_err(di->dev, "failed to enable BATT_OVV\n");
+               goto out;
+       }
+
+       /* Low Battery Voltage */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_SYS_CTRL2_BLOCK,
+               AB8500_LOW_BAT_REG,
+               ab8500_volt_to_regval(
+                       di->bat->fg_params->lowbat_threshold) << 1 |
+               LOW_BAT_ENABLE);
+       if (ret) {
+               dev_err(di->dev, "%s write failed\n", __func__);
+               goto out;
+       }
+
+       /* Battery OK threshold */
+       ret = ab8500_fg_battok_init_hw_register(di);
+       if (ret) {
+               dev_err(di->dev, "BattOk init write failed.\n");
+               goto out;
+       }
+out:
+       return ret;
+}
+
+/**
+ * ab8500_fg_external_power_changed() - callback for power supply changes
+ * @psy:       pointer to the structure power_supply
+ *
+ * This function is the entry point of the pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in any external power
+ * supply that this driver needs to be notified of.
+ */
+static void ab8500_fg_external_power_changed(struct power_supply *psy)
+{
+       struct ab8500_fg *di = to_ab8500_fg_device_info(psy);
+
+       class_for_each_device(power_supply_class, NULL,
+               &di->fg_psy, ab8500_fg_get_ext_psy_data);
+}
+
+/**
+ * abab8500_fg_reinit_work() - work to reset the FG algorithm
+ * @work:      pointer to the work_struct structure
+ *
+ * Used to reset the current battery capacity to be able to
+ * retrigger a new voltage base capacity calculation. For
+ * test and verification purpose.
+ */
+static void ab8500_fg_reinit_work(struct work_struct *work)
+{
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+               fg_reinit_work.work);
+
+       if (di->flags.calibrate == false) {
+               dev_dbg(di->dev, "Resetting FG state machine to init.\n");
+               ab8500_fg_clear_cap_samples(di);
+               ab8500_fg_calc_cap_discharge_voltage(di, true);
+               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+               ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+
+       } else {
+               dev_err(di->dev, "Residual offset calibration ongoing "
+                       "retrying..\n");
+               /* Wait one second until next try*/
+               queue_delayed_work(di->fg_wq, &di->fg_reinit_work,
+                       round_jiffies(1));
+       }
+}
+
+/**
+ * ab8500_fg_reinit() - forces FG algorithm to reinitialize with current values
+ *
+ * This function can be used to force the FG algorithm to recalculate a new
+ * voltage based battery capacity.
+ */
+void ab8500_fg_reinit(void)
+{
+       struct ab8500_fg *di = ab8500_fg_get();
+       /* User won't be notified if a null pointer returned. */
+       if (di != NULL)
+               queue_delayed_work(di->fg_wq, &di->fg_reinit_work, 0);
+}
+
+/* Exposure to the sysfs interface */
+
+struct ab8500_fg_sysfs_entry {
+       struct attribute attr;
+       ssize_t (*show)(struct ab8500_fg *, char *);
+       ssize_t (*store)(struct ab8500_fg *, const char *, size_t);
+};
+
+static ssize_t charge_full_show(struct ab8500_fg *di, char *buf)
+{
+       return sprintf(buf, "%d\n", di->bat_cap.max_mah);
+}
+
+static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf,
+                                size_t count)
+{
+       unsigned long charge_full;
+       ssize_t ret = -EINVAL;
+
+       ret = strict_strtoul(buf, 10, &charge_full);
+
+       dev_dbg(di->dev, "Ret %zd charge_full %lu", ret, charge_full);
+
+       if (!ret) {
+               di->bat_cap.max_mah = (int) charge_full;
+               ret = count;
+       }
+       return ret;
+}
+
+static ssize_t charge_now_show(struct ab8500_fg *di, char *buf)
+{
+       return sprintf(buf, "%d\n", di->bat_cap.prev_mah);
+}
+
+static ssize_t charge_now_store(struct ab8500_fg *di, const char *buf,
+                                size_t count)
+{
+       unsigned long charge_now;
+       ssize_t ret;
+
+       ret = strict_strtoul(buf, 10, &charge_now);
+
+       dev_dbg(di->dev, "Ret %zd charge_now %lu was %d",
+               ret, charge_now, di->bat_cap.prev_mah);
+
+       if (!ret) {
+               di->bat_cap.user_mah = (int) charge_now;
+               di->flags.user_cap = true;
+               ret = count;
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+       }
+       return ret;
+}
+
+static struct ab8500_fg_sysfs_entry charge_full_attr =
+       __ATTR(charge_full, 0644, charge_full_show, charge_full_store);
+
+static struct ab8500_fg_sysfs_entry charge_now_attr =
+       __ATTR(charge_now, 0644, charge_now_show, charge_now_store);
+
+static ssize_t
+ab8500_fg_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+       struct ab8500_fg_sysfs_entry *entry;
+       struct ab8500_fg *di;
+
+       entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr);
+       di = container_of(kobj, struct ab8500_fg, fg_kobject);
+
+       if (!entry->show)
+               return -EIO;
+
+       return entry->show(di, buf);
+}
+static ssize_t
+ab8500_fg_store(struct kobject *kobj, struct attribute *attr, const char *buf,
+               size_t count)
+{
+       struct ab8500_fg_sysfs_entry *entry;
+       struct ab8500_fg *di;
+
+       entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr);
+       di = container_of(kobj, struct ab8500_fg, fg_kobject);
+
+       if (!entry->store)
+               return -EIO;
+
+       return entry->store(di, buf, count);
+}
+
+static const struct sysfs_ops ab8500_fg_sysfs_ops = {
+       .show = ab8500_fg_show,
+       .store = ab8500_fg_store,
+};
+
+static struct attribute *ab8500_fg_attrs[] = {
+       &charge_full_attr.attr,
+       &charge_now_attr.attr,
+       NULL,
+};
+
+static struct kobj_type ab8500_fg_ktype = {
+       .sysfs_ops = &ab8500_fg_sysfs_ops,
+       .default_attrs = ab8500_fg_attrs,
+};
+
+/**
+ * ab8500_chargalg_sysfs_exit() - de-init of sysfs entry
+ * @di:                pointer to the struct ab8500_chargalg
+ *
+ * This function removes the entry in sysfs.
+ */
+static void ab8500_fg_sysfs_exit(struct ab8500_fg *di)
+{
+       kobject_del(&di->fg_kobject);
+}
+
+/**
+ * ab8500_chargalg_sysfs_init() - init of sysfs entry
+ * @di:                pointer to the struct ab8500_chargalg
+ *
+ * This function adds an entry in sysfs.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
+{
+       int ret = 0;
+
+       ret = kobject_init_and_add(&di->fg_kobject,
+               &ab8500_fg_ktype,
+               NULL, "battery");
+       if (ret < 0)
+               dev_err(di->dev, "failed to create sysfs entry\n");
+
+       return ret;
+}
+/* Exposure to the sysfs interface <<END>> */
+
+#if defined(CONFIG_PM)
+static int ab8500_fg_resume(struct platform_device *pdev)
+{
+       struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+       /*
+        * Change state if we're not charging. If we're charging we will wake
+        * up on the FG IRQ
+        */
+       if (!di->flags.charging) {
+               ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_WAKEUP);
+               queue_work(di->fg_wq, &di->fg_work);
+       }
+
+       return 0;
+}
+
+static int ab8500_fg_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+       flush_delayed_work(&di->fg_periodic_work);
+
+       /*
+        * If the FG is enabled we will disable it before going to suspend
+        * only if we're not charging
+        */
+       if (di->flags.fg_enabled && !di->flags.charging)
+               ab8500_fg_coulomb_counter(di, false);
+
+       return 0;
+}
+#else
+#define ab8500_fg_suspend      NULL
+#define ab8500_fg_resume       NULL
+#endif
+
+static int __devexit ab8500_fg_remove(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+       list_del(&di->node);
+
+       /* Disable coulomb counter */
+       ret = ab8500_fg_coulomb_counter(di, false);
+       if (ret)
+               dev_err(di->dev, "failed to disable coulomb counter\n");
+
+       destroy_workqueue(di->fg_wq);
+       ab8500_fg_sysfs_exit(di);
+
+       flush_scheduled_work();
+       power_supply_unregister(&di->fg_psy);
+       platform_set_drvdata(pdev, NULL);
+       kfree(di);
+       return ret;
+}
+
+/* ab8500 fg driver interrupts and their respective isr */
+static struct ab8500_fg_interrupts ab8500_fg_irq[] = {
+       {"NCONV_ACCU", ab8500_fg_cc_convend_handler},
+       {"BATT_OVV", ab8500_fg_batt_ovv_handler},
+       {"LOW_BAT_F", ab8500_fg_lowbatf_handler},
+       {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler},
+       {"CCEOC", ab8500_fg_cc_data_end_handler},
+};
+
+static int __devinit ab8500_fg_probe(struct platform_device *pdev)
+{
+       int i, irq;
+       int ret = 0;
+       struct abx500_bm_plat_data *plat_data;
+
+       struct ab8500_fg *di =
+               kzalloc(sizeof(struct ab8500_fg), GFP_KERNEL);
+       if (!di)
+               return -ENOMEM;
+
+       mutex_init(&di->cc_lock);
+
+       /* get parent data */
+       di->dev = &pdev->dev;
+       di->parent = dev_get_drvdata(pdev->dev.parent);
+       di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+       /* get fg specific platform data */
+       plat_data = pdev->dev.platform_data;
+       di->pdata = plat_data->fg;
+       if (!di->pdata) {
+               dev_err(di->dev, "no fg platform data supplied\n");
+               ret = -EINVAL;
+               goto free_device_info;
+       }
+
+       /* get battery specific platform data */
+       di->bat = plat_data->battery;
+       if (!di->bat) {
+               dev_err(di->dev, "no battery platform data supplied\n");
+               ret = -EINVAL;
+               goto free_device_info;
+       }
+
+       di->fg_psy.name = "ab8500_fg";
+       di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+       di->fg_psy.properties = ab8500_fg_props;
+       di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props);
+       di->fg_psy.get_property = ab8500_fg_get_property;
+       di->fg_psy.supplied_to = di->pdata->supplied_to;
+       di->fg_psy.num_supplicants = di->pdata->num_supplicants;
+       di->fg_psy.external_power_changed = ab8500_fg_external_power_changed;
+
+       di->bat_cap.max_mah_design = MILLI_TO_MICRO *
+               di->bat->bat_type[di->bat->batt_id].charge_full_design;
+
+       di->bat_cap.max_mah = di->bat_cap.max_mah_design;
+
+       di->vbat_nom = di->bat->bat_type[di->bat->batt_id].nominal_voltage;
+
+       di->init_capacity = true;
+
+       ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+       ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+
+       /* Create a work queue for running the FG algorithm */
+       di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
+       if (di->fg_wq == NULL) {
+               dev_err(di->dev, "failed to create work queue\n");
+               goto free_device_info;
+       }
+
+       /* Init work for running the fg algorithm instantly */
+       INIT_WORK(&di->fg_work, ab8500_fg_instant_work);
+
+       /* Init work for getting the battery accumulated current */
+       INIT_WORK(&di->fg_acc_cur_work, ab8500_fg_acc_cur_work);
+
+       /* Init work for reinitialising the fg algorithm */
+       INIT_DELAYED_WORK_DEFERRABLE(&di->fg_reinit_work,
+               ab8500_fg_reinit_work);
+
+       /* Work delayed Queue to run the state machine */
+       INIT_DELAYED_WORK_DEFERRABLE(&di->fg_periodic_work,
+               ab8500_fg_periodic_work);
+
+       /* Work to check low battery condition */
+       INIT_DELAYED_WORK_DEFERRABLE(&di->fg_low_bat_work,
+               ab8500_fg_low_bat_work);
+
+       /* Init work for HW failure check */
+       INIT_DELAYED_WORK_DEFERRABLE(&di->fg_check_hw_failure_work,
+               ab8500_fg_check_hw_failure_work);
+
+       /* Initialize OVV, and other registers */
+       ret = ab8500_fg_init_hw_registers(di);
+       if (ret) {
+               dev_err(di->dev, "failed to initialize registers\n");
+               goto free_inst_curr_wq;
+       }
+
+       /* Consider battery unknown until we're informed otherwise */
+       di->flags.batt_unknown = true;
+       di->flags.batt_id_received = false;
+
+       /* Register FG power supply class */
+       ret = power_supply_register(di->dev, &di->fg_psy);
+       if (ret) {
+               dev_err(di->dev, "failed to register FG psy\n");
+               goto free_inst_curr_wq;
+       }
+
+       di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
+       ab8500_fg_coulomb_counter(di, true);
+
+       /* Initialize completion used to notify completion of inst current */
+       init_completion(&di->ab8500_fg_complete);
+
+       /* Register interrupts */
+       for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name);
+               ret = request_threaded_irq(irq, NULL, ab8500_fg_irq[i].isr,
+                       IRQF_SHARED | IRQF_NO_SUSPEND,
+                       ab8500_fg_irq[i].name, di);
+
+               if (ret != 0) {
+                       dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+                               , ab8500_fg_irq[i].name, irq, ret);
+                       goto free_irq;
+               }
+               dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+                       ab8500_fg_irq[i].name, irq, ret);
+       }
+       di->irq = platform_get_irq_byname(pdev, "CCEOC");
+       disable_irq(di->irq);
+
+       platform_set_drvdata(pdev, di);
+
+       ret = ab8500_fg_sysfs_init(di);
+       if (ret) {
+               dev_err(di->dev, "failed to create sysfs entry\n");
+               goto free_irq;
+       }
+
+       /* Calibrate the fg first time */
+       di->flags.calibrate = true;
+       di->calib_state = AB8500_FG_CALIB_INIT;
+
+       /* Use room temp as default value until we get an update from driver. */
+       di->bat_temp = 210;
+
+       /* Run the FG algorithm */
+       queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+
+       list_add_tail(&di->node, &ab8500_fg_list);
+
+       return ret;
+
+free_irq:
+       power_supply_unregister(&di->fg_psy);
+
+       /* We also have to free all successfully registered irqs */
+       for (i = i - 1; i >= 0; i--) {
+               irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name);
+               free_irq(irq, di);
+       }
+free_inst_curr_wq:
+       destroy_workqueue(di->fg_wq);
+free_device_info:
+       kfree(di);
+
+       return ret;
+}
+
+static struct platform_driver ab8500_fg_driver = {
+       .probe = ab8500_fg_probe,
+       .remove = __devexit_p(ab8500_fg_remove),
+       .suspend = ab8500_fg_suspend,
+       .resume = ab8500_fg_resume,
+       .driver = {
+               .name = "ab8500-fg",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init ab8500_fg_init(void)
+{
+       return platform_driver_register(&ab8500_fg_driver);
+}
+
+static void __exit ab8500_fg_exit(void)
+{
+       platform_driver_unregister(&ab8500_fg_driver);
+}
+
+subsys_initcall_sync(ab8500_fg_init);
+module_exit(ab8500_fg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:ab8500-fg");
+MODULE_DESCRIPTION("AB8500 Fuel Gauge driver");
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
new file mode 100644 (file)
index 0000000..804b88c
--- /dev/null
@@ -0,0 +1,1921 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Charging algorithm driver for abx500 variants
+ *
+ * License Terms: GNU General Public License v2
+ * Authors:
+ *     Johan Palsson <johan.palsson@stericsson.com>
+ *     Karl Komierowski <karl.komierowski@stericsson.com>
+ *     Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+
+/* Watchdog kick interval */
+#define CHG_WD_INTERVAL                        (6 * HZ)
+
+/* End-of-charge criteria counter */
+#define EOC_COND_CNT                   10
+
+/* Recharge criteria counter */
+#define RCH_COND_CNT                   3
+
+#define to_abx500_chargalg_device_info(x) container_of((x), \
+       struct abx500_chargalg, chargalg_psy);
+
+enum abx500_chargers {
+       NO_CHG,
+       AC_CHG,
+       USB_CHG,
+};
+
+struct abx500_chargalg_charger_info {
+       enum abx500_chargers conn_chg;
+       enum abx500_chargers prev_conn_chg;
+       enum abx500_chargers online_chg;
+       enum abx500_chargers prev_online_chg;
+       enum abx500_chargers charger_type;
+       bool usb_chg_ok;
+       bool ac_chg_ok;
+       int usb_volt;
+       int usb_curr;
+       int ac_volt;
+       int ac_curr;
+       int usb_vset;
+       int usb_iset;
+       int ac_vset;
+       int ac_iset;
+};
+
+struct abx500_chargalg_suspension_status {
+       bool suspended_change;
+       bool ac_suspended;
+       bool usb_suspended;
+};
+
+struct abx500_chargalg_battery_data {
+       int temp;
+       int volt;
+       int avg_curr;
+       int inst_curr;
+       int percent;
+};
+
+enum abx500_chargalg_states {
+       STATE_HANDHELD_INIT,
+       STATE_HANDHELD,
+       STATE_CHG_NOT_OK_INIT,
+       STATE_CHG_NOT_OK,
+       STATE_HW_TEMP_PROTECT_INIT,
+       STATE_HW_TEMP_PROTECT,
+       STATE_NORMAL_INIT,
+       STATE_NORMAL,
+       STATE_WAIT_FOR_RECHARGE_INIT,
+       STATE_WAIT_FOR_RECHARGE,
+       STATE_MAINTENANCE_A_INIT,
+       STATE_MAINTENANCE_A,
+       STATE_MAINTENANCE_B_INIT,
+       STATE_MAINTENANCE_B,
+       STATE_TEMP_UNDEROVER_INIT,
+       STATE_TEMP_UNDEROVER,
+       STATE_TEMP_LOWHIGH_INIT,
+       STATE_TEMP_LOWHIGH,
+       STATE_SUSPENDED_INIT,
+       STATE_SUSPENDED,
+       STATE_OVV_PROTECT_INIT,
+       STATE_OVV_PROTECT,
+       STATE_SAFETY_TIMER_EXPIRED_INIT,
+       STATE_SAFETY_TIMER_EXPIRED,
+       STATE_BATT_REMOVED_INIT,
+       STATE_BATT_REMOVED,
+       STATE_WD_EXPIRED_INIT,
+       STATE_WD_EXPIRED,
+};
+
+static const char *states[] = {
+       "HANDHELD_INIT",
+       "HANDHELD",
+       "CHG_NOT_OK_INIT",
+       "CHG_NOT_OK",
+       "HW_TEMP_PROTECT_INIT",
+       "HW_TEMP_PROTECT",
+       "NORMAL_INIT",
+       "NORMAL",
+       "WAIT_FOR_RECHARGE_INIT",
+       "WAIT_FOR_RECHARGE",
+       "MAINTENANCE_A_INIT",
+       "MAINTENANCE_A",
+       "MAINTENANCE_B_INIT",
+       "MAINTENANCE_B",
+       "TEMP_UNDEROVER_INIT",
+       "TEMP_UNDEROVER",
+       "TEMP_LOWHIGH_INIT",
+       "TEMP_LOWHIGH",
+       "SUSPENDED_INIT",
+       "SUSPENDED",
+       "OVV_PROTECT_INIT",
+       "OVV_PROTECT",
+       "SAFETY_TIMER_EXPIRED_INIT",
+       "SAFETY_TIMER_EXPIRED",
+       "BATT_REMOVED_INIT",
+       "BATT_REMOVED",
+       "WD_EXPIRED_INIT",
+       "WD_EXPIRED",
+};
+
+struct abx500_chargalg_events {
+       bool batt_unknown;
+       bool mainextchnotok;
+       bool batt_ovv;
+       bool batt_rem;
+       bool btemp_underover;
+       bool btemp_lowhigh;
+       bool main_thermal_prot;
+       bool usb_thermal_prot;
+       bool main_ovv;
+       bool vbus_ovv;
+       bool usbchargernotok;
+       bool safety_timer_expired;
+       bool maintenance_timer_expired;
+       bool ac_wd_expired;
+       bool usb_wd_expired;
+       bool ac_cv_active;
+       bool usb_cv_active;
+       bool vbus_collapsed;
+};
+
+/**
+ * struct abx500_charge_curr_maximization - Charger maximization parameters
+ * @original_iset:     the non optimized/maximised charger current
+ * @current_iset:      the charging current used at this moment
+ * @test_delta_i:      the delta between the current we want to charge and the
+                       current that is really going into the battery
+ * @condition_cnt:     number of iterations needed before a new charger current
+                       is set
+ * @max_current:       maximum charger current
+ * @wait_cnt:          to avoid too fast current step down in case of charger
+ *                     voltage collapse, we insert this delay between step
+ *                     down
+ * @level:             tells in how many steps the charging current has been
+                       increased
+ */
+struct abx500_charge_curr_maximization {
+       int original_iset;
+       int current_iset;
+       int test_delta_i;
+       int condition_cnt;
+       int max_current;
+       int wait_cnt;
+       u8 level;
+};
+
+enum maxim_ret {
+       MAXIM_RET_NOACTION,
+       MAXIM_RET_CHANGE,
+       MAXIM_RET_IBAT_TOO_HIGH,
+};
+
+/**
+ * struct abx500_chargalg - abx500 Charging algorithm device information
+ * @dev:               pointer to the structure device
+ * @charge_status:     battery operating status
+ * @eoc_cnt:           counter used to determine end-of_charge
+ * @rch_cnt:           counter used to determine start of recharge
+ * @maintenance_chg:   indicate if maintenance charge is active
+ * @t_hyst_norm                temperature hysteresis when the temperature has been
+ *                     over or under normal limits
+ * @t_hyst_lowhigh     temperature hysteresis when the temperature has been
+ *                     over or under the high or low limits
+ * @charge_state:      current state of the charging algorithm
+ * @ccm                        charging current maximization parameters
+ * @chg_info:          information about connected charger types
+ * @batt_data:         data of the battery
+ * @susp_status:       current charger suspension status
+ * @pdata:             pointer to the abx500_chargalg platform data
+ * @bat:               pointer to the abx500_bm platform data
+ * @chargalg_psy:      structure that holds the battery properties exposed by
+ *                     the charging algorithm
+ * @events:            structure for information about events triggered
+ * @chargalg_wq:               work queue for running the charging algorithm
+ * @chargalg_periodic_work:    work to run the charging algorithm periodically
+ * @chargalg_wd_work:          work to kick the charger watchdog periodically
+ * @chargalg_work:             work to run the charging algorithm instantly
+ * @safety_timer:              charging safety timer
+ * @maintenance_timer:         maintenance charging timer
+ * @chargalg_kobject:          structure of type kobject
+ */
+struct abx500_chargalg {
+       struct device *dev;
+       int charge_status;
+       int eoc_cnt;
+       int rch_cnt;
+       bool maintenance_chg;
+       int t_hyst_norm;
+       int t_hyst_lowhigh;
+       enum abx500_chargalg_states charge_state;
+       struct abx500_charge_curr_maximization ccm;
+       struct abx500_chargalg_charger_info chg_info;
+       struct abx500_chargalg_battery_data batt_data;
+       struct abx500_chargalg_suspension_status susp_status;
+       struct abx500_chargalg_platform_data *pdata;
+       struct abx500_bm_data *bat;
+       struct power_supply chargalg_psy;
+       struct ux500_charger *ac_chg;
+       struct ux500_charger *usb_chg;
+       struct abx500_chargalg_events events;
+       struct workqueue_struct *chargalg_wq;
+       struct delayed_work chargalg_periodic_work;
+       struct delayed_work chargalg_wd_work;
+       struct work_struct chargalg_work;
+       struct timer_list safety_timer;
+       struct timer_list maintenance_timer;
+       struct kobject chargalg_kobject;
+};
+
+/* Main battery properties */
+static enum power_supply_property abx500_chargalg_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+};
+
+/**
+ * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
+ * @data:      pointer to the abx500_chargalg structure
+ *
+ * This function gets called when the safety timer for the charger
+ * expires
+ */
+static void abx500_chargalg_safety_timer_expired(unsigned long data)
+{
+       struct abx500_chargalg *di = (struct abx500_chargalg *) data;
+       dev_err(di->dev, "Safety timer expired\n");
+       di->events.safety_timer_expired = true;
+
+       /* Trigger execution of the algorithm instantly */
+       queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_maintenance_timer_expired() - Expiration of
+ * the maintenance timer
+ * @i:         pointer to the abx500_chargalg structure
+ *
+ * This function gets called when the maintenence timer
+ * expires
+ */
+static void abx500_chargalg_maintenance_timer_expired(unsigned long data)
+{
+
+       struct abx500_chargalg *di = (struct abx500_chargalg *) data;
+       dev_dbg(di->dev, "Maintenance timer expired\n");
+       di->events.maintenance_timer_expired = true;
+
+       /* Trigger execution of the algorithm instantly */
+       queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_state_to() - Change charge state
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This function gets called when a charge state change should occur
+ */
+static void abx500_chargalg_state_to(struct abx500_chargalg *di,
+       enum abx500_chargalg_states state)
+{
+       dev_dbg(di->dev,
+               "State changed: %s (From state: [%d] %s =to=> [%d] %s )\n",
+               di->charge_state == state ? "NO" : "YES",
+               di->charge_state,
+               states[di->charge_state],
+               state,
+               states[state]);
+
+       di->charge_state = state;
+}
+
+/**
+ * abx500_chargalg_check_charger_connection() - Check charger connection change
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This function will check if there is a change in the charger connection
+ * and change charge state accordingly. AC has precedence over USB.
+ */
+static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
+{
+       if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg ||
+               di->susp_status.suspended_change) {
+               /*
+                * Charger state changed or suspension
+                * has changed since last update
+                */
+               if ((di->chg_info.conn_chg & AC_CHG) &&
+                       !di->susp_status.ac_suspended) {
+                       dev_dbg(di->dev, "Charging source is AC\n");
+                       if (di->chg_info.charger_type != AC_CHG) {
+                               di->chg_info.charger_type = AC_CHG;
+                               abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+                       }
+               } else if ((di->chg_info.conn_chg & USB_CHG) &&
+                       !di->susp_status.usb_suspended) {
+                       dev_dbg(di->dev, "Charging source is USB\n");
+                       di->chg_info.charger_type = USB_CHG;
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               } else if (di->chg_info.conn_chg &&
+                       (di->susp_status.ac_suspended ||
+                       di->susp_status.usb_suspended)) {
+                       dev_dbg(di->dev, "Charging is suspended\n");
+                       di->chg_info.charger_type = NO_CHG;
+                       abx500_chargalg_state_to(di, STATE_SUSPENDED_INIT);
+               } else {
+                       dev_dbg(di->dev, "Charging source is OFF\n");
+                       di->chg_info.charger_type = NO_CHG;
+                       abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+               }
+               di->chg_info.prev_conn_chg = di->chg_info.conn_chg;
+               di->susp_status.suspended_change = false;
+       }
+       return di->chg_info.conn_chg;
+}
+
+/**
+ * abx500_chargalg_start_safety_timer() - Start charging safety timer
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The safety timer is used to avoid overcharging of old or bad batteries.
+ * There are different timers for AC and USB
+ */
+static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
+{
+       unsigned long timer_expiration = 0;
+
+       switch (di->chg_info.charger_type) {
+       case AC_CHG:
+               timer_expiration =
+               round_jiffies(jiffies +
+                       (di->bat->main_safety_tmr_h * 3600 * HZ));
+               break;
+
+       case USB_CHG:
+               timer_expiration =
+               round_jiffies(jiffies +
+                       (di->bat->usb_safety_tmr_h * 3600 * HZ));
+               break;
+
+       default:
+               dev_err(di->dev, "Unknown charger to charge from\n");
+               break;
+       }
+
+       di->events.safety_timer_expired = false;
+       di->safety_timer.expires = timer_expiration;
+       if (!timer_pending(&di->safety_timer))
+               add_timer(&di->safety_timer);
+       else
+               mod_timer(&di->safety_timer, timer_expiration);
+}
+
+/**
+ * abx500_chargalg_stop_safety_timer() - Stop charging safety timer
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The safety timer is stopped whenever the NORMAL state is exited
+ */
+static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
+{
+       di->events.safety_timer_expired = false;
+       del_timer(&di->safety_timer);
+}
+
+/**
+ * abx500_chargalg_start_maintenance_timer() - Start charging maintenance timer
+ * @di:                pointer to the abx500_chargalg structure
+ * @duration:  duration of ther maintenance timer in hours
+ *
+ * The maintenance timer is used to maintain the charge in the battery once
+ * the battery is considered full. These timers are chosen to match the
+ * discharge curve of the battery
+ */
+static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
+       int duration)
+{
+       unsigned long timer_expiration;
+
+       /* Convert from hours to jiffies */
+       timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ));
+
+       di->events.maintenance_timer_expired = false;
+       di->maintenance_timer.expires = timer_expiration;
+       if (!timer_pending(&di->maintenance_timer))
+               add_timer(&di->maintenance_timer);
+       else
+               mod_timer(&di->maintenance_timer, timer_expiration);
+}
+
+/**
+ * abx500_chargalg_stop_maintenance_timer() - Stop maintenance timer
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The maintenance timer is stopped whenever maintenance ends or when another
+ * state is entered
+ */
+static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
+{
+       di->events.maintenance_timer_expired = false;
+       del_timer(&di->maintenance_timer);
+}
+
+/**
+ * abx500_chargalg_kick_watchdog() - Kick charger watchdog
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The charger watchdog have to be kicked periodically whenever the charger is
+ * on, else the ABB will reset the system
+ */
+static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
+{
+       /* Check if charger exists and kick watchdog if charging */
+       if (di->ac_chg && di->ac_chg->ops.kick_wd &&
+                       di->chg_info.online_chg & AC_CHG)
+               return di->ac_chg->ops.kick_wd(di->ac_chg);
+       else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
+                       di->chg_info.online_chg & USB_CHG)
+               return di->usb_chg->ops.kick_wd(di->usb_chg);
+
+       return -ENXIO;
+}
+
+/**
+ * abx500_chargalg_ac_en() - Turn on/off the AC charger
+ * @di:                pointer to the abx500_chargalg structure
+ * @enable:    charger on/off
+ * @vset:      requested charger output voltage
+ * @iset:      requested charger output current
+ *
+ * The AC charger will be turned on/off with the requested charge voltage and
+ * current
+ */
+static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
+       int vset, int iset)
+{
+       if (!di->ac_chg || !di->ac_chg->ops.enable)
+               return -ENXIO;
+
+       /* Select maximum of what both the charger and the battery supports */
+       if (di->ac_chg->max_out_volt)
+               vset = min(vset, di->ac_chg->max_out_volt);
+       if (di->ac_chg->max_out_curr)
+               iset = min(iset, di->ac_chg->max_out_curr);
+
+       di->chg_info.ac_iset = iset;
+       di->chg_info.ac_vset = vset;
+
+       return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
+}
+
+/**
+ * abx500_chargalg_usb_en() - Turn on/off the USB charger
+ * @di:                pointer to the abx500_chargalg structure
+ * @enable:    charger on/off
+ * @vset:      requested charger output voltage
+ * @iset:      requested charger output current
+ *
+ * The USB charger will be turned on/off with the requested charge voltage and
+ * current
+ */
+static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
+       int vset, int iset)
+{
+       if (!di->usb_chg || !di->usb_chg->ops.enable)
+               return -ENXIO;
+
+       /* Select maximum of what both the charger and the battery supports */
+       if (di->usb_chg->max_out_volt)
+               vset = min(vset, di->usb_chg->max_out_volt);
+       if (di->usb_chg->max_out_curr)
+               iset = min(iset, di->usb_chg->max_out_curr);
+
+       di->chg_info.usb_iset = iset;
+       di->chg_info.usb_vset = vset;
+
+       return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
+}
+
+/**
+ * abx500_chargalg_update_chg_curr() - Update charger current
+ * @di:                pointer to the abx500_chargalg structure
+ * @iset:      requested charger output current
+ *
+ * The charger output current will be updated for the charger
+ * that is currently in use
+ */
+static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di,
+               int iset)
+{
+       /* Check if charger exists and update current if charging */
+       if (di->ac_chg && di->ac_chg->ops.update_curr &&
+                       di->chg_info.charger_type & AC_CHG) {
+               /*
+                * Select maximum of what both the charger
+                * and the battery supports
+                */
+               if (di->ac_chg->max_out_curr)
+                       iset = min(iset, di->ac_chg->max_out_curr);
+
+               di->chg_info.ac_iset = iset;
+
+               return di->ac_chg->ops.update_curr(di->ac_chg, iset);
+       } else if (di->usb_chg && di->usb_chg->ops.update_curr &&
+                       di->chg_info.charger_type & USB_CHG) {
+               /*
+                * Select maximum of what both the charger
+                * and the battery supports
+                */
+               if (di->usb_chg->max_out_curr)
+                       iset = min(iset, di->usb_chg->max_out_curr);
+
+               di->chg_info.usb_iset = iset;
+
+               return di->usb_chg->ops.update_curr(di->usb_chg, iset);
+       }
+
+       return -ENXIO;
+}
+
+/**
+ * abx500_chargalg_stop_charging() - Stop charging
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This function is called from any state where charging should be stopped.
+ * All charging is disabled and all status parameters and timers are changed
+ * accordingly
+ */
+static void abx500_chargalg_stop_charging(struct abx500_chargalg *di)
+{
+       abx500_chargalg_ac_en(di, false, 0, 0);
+       abx500_chargalg_usb_en(di, false, 0, 0);
+       abx500_chargalg_stop_safety_timer(di);
+       abx500_chargalg_stop_maintenance_timer(di);
+       di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       di->maintenance_chg = false;
+       cancel_delayed_work(&di->chargalg_wd_work);
+       power_supply_changed(&di->chargalg_psy);
+}
+
+/**
+ * abx500_chargalg_hold_charging() - Pauses charging
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This function is called in the case where maintenance charging has been
+ * disabled and instead a battery voltage mode is entered to check when the
+ * battery voltage has reached a certain recharge voltage
+ */
+static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
+{
+       abx500_chargalg_ac_en(di, false, 0, 0);
+       abx500_chargalg_usb_en(di, false, 0, 0);
+       abx500_chargalg_stop_safety_timer(di);
+       abx500_chargalg_stop_maintenance_timer(di);
+       di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+       di->maintenance_chg = false;
+       cancel_delayed_work(&di->chargalg_wd_work);
+       power_supply_changed(&di->chargalg_psy);
+}
+
+/**
+ * abx500_chargalg_start_charging() - Start the charger
+ * @di:                pointer to the abx500_chargalg structure
+ * @vset:      requested charger output voltage
+ * @iset:      requested charger output current
+ *
+ * A charger will be enabled depending on the requested charger type that was
+ * detected previously.
+ */
+static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
+       int vset, int iset)
+{
+       switch (di->chg_info.charger_type) {
+       case AC_CHG:
+               dev_dbg(di->dev,
+                       "AC parameters: Vset %d, Ich %d\n", vset, iset);
+               abx500_chargalg_usb_en(di, false, 0, 0);
+               abx500_chargalg_ac_en(di, true, vset, iset);
+               break;
+
+       case USB_CHG:
+               dev_dbg(di->dev,
+                       "USB parameters: Vset %d, Ich %d\n", vset, iset);
+               abx500_chargalg_ac_en(di, false, 0, 0);
+               abx500_chargalg_usb_en(di, true, vset, iset);
+               break;
+
+       default:
+               dev_err(di->dev, "Unknown charger to charge from\n");
+               break;
+       }
+}
+
+/**
+ * abx500_chargalg_check_temp() - Check battery temperature ranges
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The battery temperature is checked against the predefined limits and the
+ * charge state is changed accordingly
+ */
+static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
+{
+       if (di->batt_data.temp > (di->bat->temp_low + di->t_hyst_norm) &&
+               di->batt_data.temp < (di->bat->temp_high - di->t_hyst_norm)) {
+               /* Temp OK! */
+               di->events.btemp_underover = false;
+               di->events.btemp_lowhigh = false;
+               di->t_hyst_norm = 0;
+               di->t_hyst_lowhigh = 0;
+       } else {
+               if (((di->batt_data.temp >= di->bat->temp_high) &&
+                       (di->batt_data.temp <
+                               (di->bat->temp_over - di->t_hyst_lowhigh))) ||
+                       ((di->batt_data.temp >
+                               (di->bat->temp_under + di->t_hyst_lowhigh)) &&
+                       (di->batt_data.temp <= di->bat->temp_low))) {
+                       /* TEMP minor!!!!! */
+                       di->events.btemp_underover = false;
+                       di->events.btemp_lowhigh = true;
+                       di->t_hyst_norm = di->bat->temp_hysteresis;
+                       di->t_hyst_lowhigh = 0;
+               } else if (di->batt_data.temp <= di->bat->temp_under ||
+                       di->batt_data.temp >= di->bat->temp_over) {
+                       /* TEMP major!!!!! */
+                       di->events.btemp_underover = true;
+                       di->events.btemp_lowhigh = false;
+                       di->t_hyst_norm = 0;
+                       di->t_hyst_lowhigh = di->bat->temp_hysteresis;
+               } else {
+               /* Within hysteresis */
+               dev_dbg(di->dev, "Within hysteresis limit temp: %d "
+                               "hyst_lowhigh %d, hyst normal %d\n",
+                               di->batt_data.temp, di->t_hyst_lowhigh,
+                               di->t_hyst_norm);
+               }
+       }
+}
+
+/**
+ * abx500_chargalg_check_charger_voltage() - Check charger voltage
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * Charger voltage is checked against maximum limit
+ */
+static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
+{
+       if (di->chg_info.usb_volt > di->bat->chg_params->usb_volt_max)
+               di->chg_info.usb_chg_ok = false;
+       else
+               di->chg_info.usb_chg_ok = true;
+
+       if (di->chg_info.ac_volt > di->bat->chg_params->ac_volt_max)
+               di->chg_info.ac_chg_ok = false;
+       else
+               di->chg_info.ac_chg_ok = true;
+
+}
+
+/**
+ * abx500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * End-of-charge criteria is fulfilled when the battery voltage is above a
+ * certain limit and the battery current is below a certain limit for a
+ * predefined number of consecutive seconds. If true, the battery is full
+ */
+static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
+{
+       if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
+               di->charge_state == STATE_NORMAL &&
+               !di->maintenance_chg && (di->batt_data.volt >=
+               di->bat->bat_type[di->bat->batt_id].termination_vol ||
+               di->events.usb_cv_active || di->events.ac_cv_active) &&
+               di->batt_data.avg_curr <
+               di->bat->bat_type[di->bat->batt_id].termination_curr &&
+               di->batt_data.avg_curr > 0) {
+               if (++di->eoc_cnt >= EOC_COND_CNT) {
+                       di->eoc_cnt = 0;
+                       di->charge_status = POWER_SUPPLY_STATUS_FULL;
+                       di->maintenance_chg = true;
+                       dev_dbg(di->dev, "EOC reached!\n");
+                       power_supply_changed(&di->chargalg_psy);
+               } else {
+                       dev_dbg(di->dev,
+                               " EOC limit reached for the %d"
+                               " time, out of %d before EOC\n",
+                               di->eoc_cnt,
+                               EOC_COND_CNT);
+               }
+       } else {
+               di->eoc_cnt = 0;
+       }
+}
+
+static void init_maxim_chg_curr(struct abx500_chargalg *di)
+{
+       di->ccm.original_iset =
+               di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
+       di->ccm.current_iset =
+               di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
+       di->ccm.test_delta_i = di->bat->maxi->charger_curr_step;
+       di->ccm.max_current = di->bat->maxi->chg_curr;
+       di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+       di->ccm.level = 0;
+}
+
+/**
+ * abx500_chargalg_chg_curr_maxim - increases the charger current to
+ *                     compensate for the system load
+ * @di         pointer to the abx500_chargalg structure
+ *
+ * This maximization function is used to raise the charger current to get the
+ * battery current as close to the optimal value as possible. The battery
+ * current during charging is affected by the system load
+ */
+static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
+{
+       int delta_i;
+
+       if (!di->bat->maxi->ena_maxi)
+               return MAXIM_RET_NOACTION;
+
+       delta_i = di->ccm.original_iset - di->batt_data.inst_curr;
+
+       if (di->events.vbus_collapsed) {
+               dev_dbg(di->dev, "Charger voltage has collapsed %d\n",
+                               di->ccm.wait_cnt);
+               if (di->ccm.wait_cnt == 0) {
+                       dev_dbg(di->dev, "lowering current\n");
+                       di->ccm.wait_cnt++;
+                       di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+                       di->ccm.max_current =
+                               di->ccm.current_iset - di->ccm.test_delta_i;
+                       di->ccm.current_iset = di->ccm.max_current;
+                       di->ccm.level--;
+                       return MAXIM_RET_CHANGE;
+               } else {
+                       dev_dbg(di->dev, "waiting\n");
+                       /* Let's go in here twice before lowering curr again */
+                       di->ccm.wait_cnt = (di->ccm.wait_cnt + 1) % 3;
+                       return MAXIM_RET_NOACTION;
+               }
+       }
+
+       di->ccm.wait_cnt = 0;
+
+       if ((di->batt_data.inst_curr > di->ccm.original_iset)) {
+               dev_dbg(di->dev, " Maximization Ibat (%dmA) too high"
+                       " (limit %dmA) (current iset: %dmA)!\n",
+                       di->batt_data.inst_curr, di->ccm.original_iset,
+                       di->ccm.current_iset);
+
+               if (di->ccm.current_iset == di->ccm.original_iset)
+                       return MAXIM_RET_NOACTION;
+
+               di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+               di->ccm.current_iset = di->ccm.original_iset;
+               di->ccm.level = 0;
+
+               return MAXIM_RET_IBAT_TOO_HIGH;
+       }
+
+       if (delta_i > di->ccm.test_delta_i &&
+               (di->ccm.current_iset + di->ccm.test_delta_i) <
+               di->ccm.max_current) {
+               if (di->ccm.condition_cnt-- == 0) {
+                       /* Increse the iset with cco.test_delta_i */
+                       di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+                       di->ccm.current_iset += di->ccm.test_delta_i;
+                       di->ccm.level++;
+                       dev_dbg(di->dev, " Maximization needed, increase"
+                               " with %d mA to %dmA (Optimal ibat: %d)"
+                               " Level %d\n",
+                               di->ccm.test_delta_i,
+                               di->ccm.current_iset,
+                               di->ccm.original_iset,
+                               di->ccm.level);
+                       return MAXIM_RET_CHANGE;
+               } else {
+                       return MAXIM_RET_NOACTION;
+               }
+       }  else {
+               di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+               return MAXIM_RET_NOACTION;
+       }
+}
+
+static void handle_maxim_chg_curr(struct abx500_chargalg *di)
+{
+       enum maxim_ret ret;
+       int result;
+
+       ret = abx500_chargalg_chg_curr_maxim(di);
+       switch (ret) {
+       case MAXIM_RET_CHANGE:
+               result = abx500_chargalg_update_chg_curr(di,
+                       di->ccm.current_iset);
+               if (result)
+                       dev_err(di->dev, "failed to set chg curr\n");
+               break;
+       case MAXIM_RET_IBAT_TOO_HIGH:
+               result = abx500_chargalg_update_chg_curr(di,
+                       di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
+               if (result)
+                       dev_err(di->dev, "failed to set chg curr\n");
+               break;
+
+       case MAXIM_RET_NOACTION:
+       default:
+               /* Do nothing..*/
+               break;
+       }
+}
+
+static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
+{
+       struct power_supply *psy;
+       struct power_supply *ext;
+       struct abx500_chargalg *di;
+       union power_supply_propval ret;
+       int i, j;
+       bool psy_found = false;
+
+       psy = (struct power_supply *)data;
+       ext = dev_get_drvdata(dev);
+       di = to_abx500_chargalg_device_info(psy);
+       /* For all psy where the driver name appears in any supplied_to */
+       for (i = 0; i < ext->num_supplicants; i++) {
+               if (!strcmp(ext->supplied_to[i], psy->name))
+                       psy_found = true;
+       }
+       if (!psy_found)
+               return 0;
+
+       /* Go through all properties for the psy */
+       for (j = 0; j < ext->num_properties; j++) {
+               enum power_supply_property prop;
+               prop = ext->properties[j];
+
+               /* Initialize chargers if not already done */
+               if (!di->ac_chg &&
+                       ext->type == POWER_SUPPLY_TYPE_MAINS)
+                       di->ac_chg = psy_to_ux500_charger(ext);
+               else if (!di->usb_chg &&
+                       ext->type == POWER_SUPPLY_TYPE_USB)
+                       di->usb_chg = psy_to_ux500_charger(ext);
+
+               if (ext->get_property(ext, prop, &ret))
+                       continue;
+               switch (prop) {
+               case POWER_SUPPLY_PROP_PRESENT:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               /* Battery present */
+                               if (ret.intval)
+                                       di->events.batt_rem = false;
+                               /* Battery removed */
+                               else
+                                       di->events.batt_rem = true;
+                               break;
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               /* AC disconnected */
+                               if (!ret.intval &&
+                                       (di->chg_info.conn_chg & AC_CHG)) {
+                                       di->chg_info.prev_conn_chg =
+                                               di->chg_info.conn_chg;
+                                       di->chg_info.conn_chg &= ~AC_CHG;
+                               }
+                               /* AC connected */
+                               else if (ret.intval &&
+                                       !(di->chg_info.conn_chg & AC_CHG)) {
+                                       di->chg_info.prev_conn_chg =
+                                               di->chg_info.conn_chg;
+                                       di->chg_info.conn_chg |= AC_CHG;
+                               }
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               /* USB disconnected */
+                               if (!ret.intval &&
+                                       (di->chg_info.conn_chg & USB_CHG)) {
+                                       di->chg_info.prev_conn_chg =
+                                               di->chg_info.conn_chg;
+                                       di->chg_info.conn_chg &= ~USB_CHG;
+                               }
+                               /* USB connected */
+                               else if (ret.intval &&
+                                       !(di->chg_info.conn_chg & USB_CHG)) {
+                                       di->chg_info.prev_conn_chg =
+                                               di->chg_info.conn_chg;
+                                       di->chg_info.conn_chg |= USB_CHG;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_ONLINE:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               break;
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               /* AC offline */
+                               if (!ret.intval &&
+                                       (di->chg_info.online_chg & AC_CHG)) {
+                                       di->chg_info.prev_online_chg =
+                                               di->chg_info.online_chg;
+                                       di->chg_info.online_chg &= ~AC_CHG;
+                               }
+                               /* AC online */
+                               else if (ret.intval &&
+                                       !(di->chg_info.online_chg & AC_CHG)) {
+                                       di->chg_info.prev_online_chg =
+                                               di->chg_info.online_chg;
+                                       di->chg_info.online_chg |= AC_CHG;
+                                       queue_delayed_work(di->chargalg_wq,
+                                               &di->chargalg_wd_work, 0);
+                               }
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               /* USB offline */
+                               if (!ret.intval &&
+                                       (di->chg_info.online_chg & USB_CHG)) {
+                                       di->chg_info.prev_online_chg =
+                                               di->chg_info.online_chg;
+                                       di->chg_info.online_chg &= ~USB_CHG;
+                               }
+                               /* USB online */
+                               else if (ret.intval &&
+                                       !(di->chg_info.online_chg & USB_CHG)) {
+                                       di->chg_info.prev_online_chg =
+                                               di->chg_info.online_chg;
+                                       di->chg_info.online_chg |= USB_CHG;
+                                       queue_delayed_work(di->chargalg_wq,
+                                               &di->chargalg_wd_work, 0);
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_HEALTH:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               break;
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               switch (ret.intval) {
+                               case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+                                       di->events.mainextchnotok = true;
+                                       di->events.main_thermal_prot = false;
+                                       di->events.main_ovv = false;
+                                       di->events.ac_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_DEAD:
+                                       di->events.ac_wd_expired = true;
+                                       di->events.mainextchnotok = false;
+                                       di->events.main_ovv = false;
+                                       di->events.main_thermal_prot = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_COLD:
+                               case POWER_SUPPLY_HEALTH_OVERHEAT:
+                                       di->events.main_thermal_prot = true;
+                                       di->events.mainextchnotok = false;
+                                       di->events.main_ovv = false;
+                                       di->events.ac_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+                                       di->events.main_ovv = true;
+                                       di->events.mainextchnotok = false;
+                                       di->events.main_thermal_prot = false;
+                                       di->events.ac_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_GOOD:
+                                       di->events.main_thermal_prot = false;
+                                       di->events.mainextchnotok = false;
+                                       di->events.main_ovv = false;
+                                       di->events.ac_wd_expired = false;
+                                       break;
+                               default:
+                                       break;
+                               }
+                               break;
+
+                       case POWER_SUPPLY_TYPE_USB:
+                               switch (ret.intval) {
+                               case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+                                       di->events.usbchargernotok = true;
+                                       di->events.usb_thermal_prot = false;
+                                       di->events.vbus_ovv = false;
+                                       di->events.usb_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_DEAD:
+                                       di->events.usb_wd_expired = true;
+                                       di->events.usbchargernotok = false;
+                                       di->events.usb_thermal_prot = false;
+                                       di->events.vbus_ovv = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_COLD:
+                               case POWER_SUPPLY_HEALTH_OVERHEAT:
+                                       di->events.usb_thermal_prot = true;
+                                       di->events.usbchargernotok = false;
+                                       di->events.vbus_ovv = false;
+                                       di->events.usb_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+                                       di->events.vbus_ovv = true;
+                                       di->events.usbchargernotok = false;
+                                       di->events.usb_thermal_prot = false;
+                                       di->events.usb_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_GOOD:
+                                       di->events.usbchargernotok = false;
+                                       di->events.usb_thermal_prot = false;
+                                       di->events.vbus_ovv = false;
+                                       di->events.usb_wd_expired = false;
+                                       break;
+                               default:
+                                       break;
+                               }
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               di->batt_data.volt = ret.intval / 1000;
+                               break;
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               di->chg_info.ac_volt = ret.intval / 1000;
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               di->chg_info.usb_volt = ret.intval / 1000;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               /* AVG is used to indicate when we are
+                                * in CV mode */
+                               if (ret.intval)
+                                       di->events.ac_cv_active = true;
+                               else
+                                       di->events.ac_cv_active = false;
+
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               /* AVG is used to indicate when we are
+                                * in CV mode */
+                               if (ret.intval)
+                                       di->events.usb_cv_active = true;
+                               else
+                                       di->events.usb_cv_active = false;
+
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_TECHNOLOGY:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               if (ret.intval)
+                                       di->events.batt_unknown = false;
+                               else
+                                       di->events.batt_unknown = true;
+
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_TEMP:
+                       di->batt_data.temp = ret.intval / 10;
+                       break;
+
+               case POWER_SUPPLY_PROP_CURRENT_NOW:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_MAINS:
+                                       di->chg_info.ac_curr =
+                                               ret.intval / 1000;
+                                       break;
+                       case POWER_SUPPLY_TYPE_USB:
+                                       di->chg_info.usb_curr =
+                                               ret.intval / 1000;
+                               break;
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               di->batt_data.inst_curr = ret.intval / 1000;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_CURRENT_AVG:
+                       switch (ext->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               di->batt_data.avg_curr = ret.intval / 1000;
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               if (ret.intval)
+                                       di->events.vbus_collapsed = true;
+                               else
+                                       di->events.vbus_collapsed = false;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case POWER_SUPPLY_PROP_CAPACITY:
+                       di->batt_data.percent = ret.intval;
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+/**
+ * abx500_chargalg_external_power_changed() - callback for power supply changes
+ * @psy:       pointer to the structure power_supply
+ *
+ * This function is the entry point of the pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in any external power
+ * supply that this driver needs to be notified of.
+ */
+static void abx500_chargalg_external_power_changed(struct power_supply *psy)
+{
+       struct abx500_chargalg *di = to_abx500_chargalg_device_info(psy);
+
+       /*
+        * Trigger execution of the algorithm instantly and read
+        * all power_supply properties there instead
+        */
+       queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_algorithm() - Main function for the algorithm
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This is the main control function for the charging algorithm.
+ * It is called periodically or when something happens that will
+ * trigger a state change
+ */
+static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
+{
+       int charger_status;
+
+       /* Collect data from all power_supply class devices */
+       class_for_each_device(power_supply_class, NULL,
+               &di->chargalg_psy, abx500_chargalg_get_ext_psy_data);
+
+       abx500_chargalg_end_of_charge(di);
+       abx500_chargalg_check_temp(di);
+       abx500_chargalg_check_charger_voltage(di);
+
+       charger_status = abx500_chargalg_check_charger_connection(di);
+       /*
+        * First check if we have a charger connected.
+        * Also we don't allow charging of unknown batteries if configured
+        * this way
+        */
+       if (!charger_status ||
+               (di->events.batt_unknown && !di->bat->chg_unknown_bat)) {
+               if (di->charge_state != STATE_HANDHELD) {
+                       di->events.safety_timer_expired = false;
+                       abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+               }
+       }
+
+       /* If suspended, we should not continue checking the flags */
+       else if (di->charge_state == STATE_SUSPENDED_INIT ||
+               di->charge_state == STATE_SUSPENDED) {
+               /* We don't do anything here, just don,t continue */
+       }
+
+       /* Safety timer expiration */
+       else if (di->events.safety_timer_expired) {
+               if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED)
+                       abx500_chargalg_state_to(di,
+                               STATE_SAFETY_TIMER_EXPIRED_INIT);
+       }
+       /*
+        * Check if any interrupts has occured
+        * that will prevent us from charging
+        */
+
+       /* Battery removed */
+       else if (di->events.batt_rem) {
+               if (di->charge_state != STATE_BATT_REMOVED)
+                       abx500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT);
+       }
+       /* Main or USB charger not ok. */
+       else if (di->events.mainextchnotok || di->events.usbchargernotok) {
+               /*
+                * If vbus_collapsed is set, we have to lower the charger
+                * current, which is done in the normal state below
+                */
+               if (di->charge_state != STATE_CHG_NOT_OK &&
+                               !di->events.vbus_collapsed)
+                       abx500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
+       }
+       /* VBUS, Main or VBAT OVV. */
+       else if (di->events.vbus_ovv ||
+                       di->events.main_ovv ||
+                       di->events.batt_ovv ||
+                       !di->chg_info.usb_chg_ok ||
+                       !di->chg_info.ac_chg_ok) {
+               if (di->charge_state != STATE_OVV_PROTECT)
+                       abx500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT);
+       }
+       /* USB Thermal, stop charging */
+       else if (di->events.main_thermal_prot ||
+               di->events.usb_thermal_prot) {
+               if (di->charge_state != STATE_HW_TEMP_PROTECT)
+                       abx500_chargalg_state_to(di,
+                               STATE_HW_TEMP_PROTECT_INIT);
+       }
+       /* Battery temp over/under */
+       else if (di->events.btemp_underover) {
+               if (di->charge_state != STATE_TEMP_UNDEROVER)
+                       abx500_chargalg_state_to(di,
+                               STATE_TEMP_UNDEROVER_INIT);
+       }
+       /* Watchdog expired */
+       else if (di->events.ac_wd_expired ||
+               di->events.usb_wd_expired) {
+               if (di->charge_state != STATE_WD_EXPIRED)
+                       abx500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT);
+       }
+       /* Battery temp high/low */
+       else if (di->events.btemp_lowhigh) {
+               if (di->charge_state != STATE_TEMP_LOWHIGH)
+                       abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT);
+       }
+
+       dev_dbg(di->dev,
+               "[CHARGALG] Vb %d Ib_avg %d Ib_inst %d Tb %d Cap %d Maint %d "
+               "State %s Active_chg %d Chg_status %d AC %d USB %d "
+               "AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d "
+               "USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n",
+               di->batt_data.volt,
+               di->batt_data.avg_curr,
+               di->batt_data.inst_curr,
+               di->batt_data.temp,
+               di->batt_data.percent,
+               di->maintenance_chg,
+               states[di->charge_state],
+               di->chg_info.charger_type,
+               di->charge_status,
+               di->chg_info.conn_chg & AC_CHG,
+               di->chg_info.conn_chg & USB_CHG,
+               di->chg_info.online_chg & AC_CHG,
+               di->chg_info.online_chg & USB_CHG,
+               di->events.ac_cv_active,
+               di->events.usb_cv_active,
+               di->chg_info.ac_curr,
+               di->chg_info.usb_curr,
+               di->chg_info.ac_vset,
+               di->chg_info.ac_iset,
+               di->chg_info.usb_vset,
+               di->chg_info.usb_iset);
+
+       switch (di->charge_state) {
+       case STATE_HANDHELD_INIT:
+               abx500_chargalg_stop_charging(di);
+               di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+               abx500_chargalg_state_to(di, STATE_HANDHELD);
+               /* Intentional fallthrough */
+
+       case STATE_HANDHELD:
+               break;
+
+       case STATE_SUSPENDED_INIT:
+               if (di->susp_status.ac_suspended)
+                       abx500_chargalg_ac_en(di, false, 0, 0);
+               if (di->susp_status.usb_suspended)
+                       abx500_chargalg_usb_en(di, false, 0, 0);
+               abx500_chargalg_stop_safety_timer(di);
+               abx500_chargalg_stop_maintenance_timer(di);
+               di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               di->maintenance_chg = false;
+               abx500_chargalg_state_to(di, STATE_SUSPENDED);
+               power_supply_changed(&di->chargalg_psy);
+               /* Intentional fallthrough */
+
+       case STATE_SUSPENDED:
+               /* CHARGING is suspended */
+               break;
+
+       case STATE_BATT_REMOVED_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_BATT_REMOVED);
+               /* Intentional fallthrough */
+
+       case STATE_BATT_REMOVED:
+               if (!di->events.batt_rem)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_HW_TEMP_PROTECT_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT);
+               /* Intentional fallthrough */
+
+       case STATE_HW_TEMP_PROTECT:
+               if (!di->events.main_thermal_prot &&
+                               !di->events.usb_thermal_prot)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_OVV_PROTECT_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_OVV_PROTECT);
+               /* Intentional fallthrough */
+
+       case STATE_OVV_PROTECT:
+               if (!di->events.vbus_ovv &&
+                               !di->events.main_ovv &&
+                               !di->events.batt_ovv &&
+                               di->chg_info.usb_chg_ok &&
+                               di->chg_info.ac_chg_ok)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_CHG_NOT_OK_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_CHG_NOT_OK);
+               /* Intentional fallthrough */
+
+       case STATE_CHG_NOT_OK:
+               if (!di->events.mainextchnotok &&
+                               !di->events.usbchargernotok)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_SAFETY_TIMER_EXPIRED_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED);
+               /* Intentional fallthrough */
+
+       case STATE_SAFETY_TIMER_EXPIRED:
+               /* We exit this state when charger is removed */
+               break;
+
+       case STATE_NORMAL_INIT:
+               abx500_chargalg_start_charging(di,
+                       di->bat->bat_type[di->bat->batt_id].normal_vol_lvl,
+                       di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
+               abx500_chargalg_state_to(di, STATE_NORMAL);
+               abx500_chargalg_start_safety_timer(di);
+               abx500_chargalg_stop_maintenance_timer(di);
+               init_maxim_chg_curr(di);
+               di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+               di->eoc_cnt = 0;
+               di->maintenance_chg = false;
+               power_supply_changed(&di->chargalg_psy);
+
+               break;
+
+       case STATE_NORMAL:
+               handle_maxim_chg_curr(di);
+               if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
+                       di->maintenance_chg) {
+                       if (di->bat->no_maintenance)
+                               abx500_chargalg_state_to(di,
+                                       STATE_WAIT_FOR_RECHARGE_INIT);
+                       else
+                               abx500_chargalg_state_to(di,
+                                       STATE_MAINTENANCE_A_INIT);
+               }
+               break;
+
+       /* This state will be used when the maintenance state is disabled */
+       case STATE_WAIT_FOR_RECHARGE_INIT:
+               abx500_chargalg_hold_charging(di);
+               abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
+               di->rch_cnt = RCH_COND_CNT;
+               /* Intentional fallthrough */
+
+       case STATE_WAIT_FOR_RECHARGE:
+               if (di->batt_data.volt <=
+                       di->bat->bat_type[di->bat->batt_id].recharge_vol) {
+                       if (di->rch_cnt-- == 0)
+                               abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               } else
+                       di->rch_cnt = RCH_COND_CNT;
+               break;
+
+       case STATE_MAINTENANCE_A_INIT:
+               abx500_chargalg_stop_safety_timer(di);
+               abx500_chargalg_start_maintenance_timer(di,
+                       di->bat->bat_type[
+                               di->bat->batt_id].maint_a_chg_timer_h);
+               abx500_chargalg_start_charging(di,
+                       di->bat->bat_type[
+                               di->bat->batt_id].maint_a_vol_lvl,
+                       di->bat->bat_type[
+                               di->bat->batt_id].maint_a_cur_lvl);
+               abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
+               power_supply_changed(&di->chargalg_psy);
+               /* Intentional fallthrough*/
+
+       case STATE_MAINTENANCE_A:
+               if (di->events.maintenance_timer_expired) {
+                       abx500_chargalg_stop_maintenance_timer(di);
+                       abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
+               }
+               break;
+
+       case STATE_MAINTENANCE_B_INIT:
+               abx500_chargalg_start_maintenance_timer(di,
+                       di->bat->bat_type[
+                               di->bat->batt_id].maint_b_chg_timer_h);
+               abx500_chargalg_start_charging(di,
+                       di->bat->bat_type[
+                               di->bat->batt_id].maint_b_vol_lvl,
+                       di->bat->bat_type[
+                               di->bat->batt_id].maint_b_cur_lvl);
+               abx500_chargalg_state_to(di, STATE_MAINTENANCE_B);
+               power_supply_changed(&di->chargalg_psy);
+               /* Intentional fallthrough*/
+
+       case STATE_MAINTENANCE_B:
+               if (di->events.maintenance_timer_expired) {
+                       abx500_chargalg_stop_maintenance_timer(di);
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               }
+               break;
+
+       case STATE_TEMP_LOWHIGH_INIT:
+               abx500_chargalg_start_charging(di,
+                       di->bat->bat_type[
+                               di->bat->batt_id].low_high_vol_lvl,
+                       di->bat->bat_type[
+                               di->bat->batt_id].low_high_cur_lvl);
+               abx500_chargalg_stop_maintenance_timer(di);
+               di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+               abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
+               power_supply_changed(&di->chargalg_psy);
+               /* Intentional fallthrough */
+
+       case STATE_TEMP_LOWHIGH:
+               if (!di->events.btemp_lowhigh)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_WD_EXPIRED_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_WD_EXPIRED);
+               /* Intentional fallthrough */
+
+       case STATE_WD_EXPIRED:
+               if (!di->events.ac_wd_expired &&
+                               !di->events.usb_wd_expired)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_TEMP_UNDEROVER_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_TEMP_UNDEROVER);
+               /* Intentional fallthrough */
+
+       case STATE_TEMP_UNDEROVER:
+               if (!di->events.btemp_underover)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+       }
+
+       /* Start charging directly if the new state is a charge state */
+       if (di->charge_state == STATE_NORMAL_INIT ||
+                       di->charge_state == STATE_MAINTENANCE_A_INIT ||
+                       di->charge_state == STATE_MAINTENANCE_B_INIT)
+               queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_periodic_work() - Periodic work for the algorithm
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for the charging algorithm
+ */
+static void abx500_chargalg_periodic_work(struct work_struct *work)
+{
+       struct abx500_chargalg *di = container_of(work,
+               struct abx500_chargalg, chargalg_periodic_work.work);
+
+       abx500_chargalg_algorithm(di);
+
+       /*
+        * If a charger is connected then the battery has to be monitored
+        * frequently, else the work can be delayed.
+        */
+       if (di->chg_info.conn_chg)
+               queue_delayed_work(di->chargalg_wq,
+                       &di->chargalg_periodic_work,
+                       di->bat->interval_charging * HZ);
+       else
+               queue_delayed_work(di->chargalg_wq,
+                       &di->chargalg_periodic_work,
+                       di->bat->interval_not_charging * HZ);
+}
+
+/**
+ * abx500_chargalg_wd_work() - periodic work to kick the charger watchdog
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for kicking the charger watchdog
+ */
+static void abx500_chargalg_wd_work(struct work_struct *work)
+{
+       int ret;
+       struct abx500_chargalg *di = container_of(work,
+               struct abx500_chargalg, chargalg_wd_work.work);
+
+       dev_dbg(di->dev, "abx500_chargalg_wd_work\n");
+
+       ret = abx500_chargalg_kick_watchdog(di);
+       if (ret < 0)
+               dev_err(di->dev, "failed to kick watchdog\n");
+
+       queue_delayed_work(di->chargalg_wq,
+               &di->chargalg_wd_work, CHG_WD_INTERVAL);
+}
+
+/**
+ * abx500_chargalg_work() - Work to run the charging algorithm instantly
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for calling the charging algorithm
+ */
+static void abx500_chargalg_work(struct work_struct *work)
+{
+       struct abx500_chargalg *di = container_of(work,
+               struct abx500_chargalg, chargalg_work);
+
+       abx500_chargalg_algorithm(di);
+}
+
+/**
+ * abx500_chargalg_get_property() - get the chargalg properties
+ * @psy:       pointer to the power_supply structure
+ * @psp:       pointer to the power_supply_property structure
+ * @val:       pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the
+ * chargalg properties by reading the sysfs files.
+ * status:     charging/discharging/full/unknown
+ * health:     health of the battery
+ * Returns error code in case of failure else 0 on success
+ */
+static int abx500_chargalg_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct abx500_chargalg *di;
+
+       di = to_abx500_chargalg_device_info(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = di->charge_status;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (di->events.batt_ovv) {
+                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               } else if (di->events.btemp_underover) {
+                       if (di->batt_data.temp <= di->bat->temp_under)
+                               val->intval = POWER_SUPPLY_HEALTH_COLD;
+                       else
+                               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               } else {
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/* Exposure to the sysfs interface */
+
+/**
+ * abx500_chargalg_sysfs_charger() - sysfs store operations
+ * @kobj:      pointer to the struct kobject
+ * @attr:      pointer to the struct attribute
+ * @buf:       buffer that holds the parameter passed from userspace
+ * @length:    length of the parameter passed
+ *
+ * Returns length of the buffer(input taken from user space) on success
+ * else error code on failure
+ * The operation to be performed on passing the parameters from the user space.
+ */
+static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
+       struct attribute *attr, const char *buf, size_t length)
+{
+       struct abx500_chargalg *di = container_of(kobj,
+               struct abx500_chargalg, chargalg_kobject);
+       long int param;
+       int ac_usb;
+       int ret;
+       char entry = *attr->name;
+
+       switch (entry) {
+       case 'c':
+               ret = strict_strtol(buf, 10, &param);
+               if (ret < 0)
+                       return ret;
+
+               ac_usb = param;
+               switch (ac_usb) {
+               case 0:
+                       /* Disable charging */
+                       di->susp_status.ac_suspended = true;
+                       di->susp_status.usb_suspended = true;
+                       di->susp_status.suspended_change = true;
+                       /* Trigger a state change */
+                       queue_work(di->chargalg_wq,
+                               &di->chargalg_work);
+                       break;
+               case 1:
+                       /* Enable AC Charging */
+                       di->susp_status.ac_suspended = false;
+                       di->susp_status.suspended_change = true;
+                       /* Trigger a state change */
+                       queue_work(di->chargalg_wq,
+                               &di->chargalg_work);
+                       break;
+               case 2:
+                       /* Enable USB charging */
+                       di->susp_status.usb_suspended = false;
+                       di->susp_status.suspended_change = true;
+                       /* Trigger a state change */
+                       queue_work(di->chargalg_wq,
+                               &di->chargalg_work);
+                       break;
+               default:
+                       dev_info(di->dev, "Wrong input\n"
+                               "Enter 0. Disable AC/USB Charging\n"
+                               "1. Enable AC charging\n"
+                               "2. Enable USB Charging\n");
+               };
+               break;
+       };
+       return strlen(buf);
+}
+
+static struct attribute abx500_chargalg_en_charger = \
+{
+       .name = "chargalg",
+       .mode = S_IWUGO,
+};
+
+static struct attribute *abx500_chargalg_chg[] = {
+       &abx500_chargalg_en_charger,
+       NULL
+};
+
+static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
+       .store = abx500_chargalg_sysfs_charger,
+};
+
+static struct kobj_type abx500_chargalg_ktype = {
+       .sysfs_ops = &abx500_chargalg_sysfs_ops,
+       .default_attrs = abx500_chargalg_chg,
+};
+
+/**
+ * abx500_chargalg_sysfs_exit() - de-init of sysfs entry
+ * @di:                pointer to the struct abx500_chargalg
+ *
+ * This function removes the entry in sysfs.
+ */
+static void abx500_chargalg_sysfs_exit(struct abx500_chargalg *di)
+{
+       kobject_del(&di->chargalg_kobject);
+}
+
+/**
+ * abx500_chargalg_sysfs_init() - init of sysfs entry
+ * @di:                pointer to the struct abx500_chargalg
+ *
+ * This function adds an entry in sysfs.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di)
+{
+       int ret = 0;
+
+       ret = kobject_init_and_add(&di->chargalg_kobject,
+               &abx500_chargalg_ktype,
+               NULL, "abx500_chargalg");
+       if (ret < 0)
+               dev_err(di->dev, "failed to create sysfs entry\n");
+
+       return ret;
+}
+/* Exposure to the sysfs interface <<END>> */
+
+#if defined(CONFIG_PM)
+static int abx500_chargalg_resume(struct platform_device *pdev)
+{
+       struct abx500_chargalg *di = platform_get_drvdata(pdev);
+
+       /* Kick charger watchdog if charging (any charger online) */
+       if (di->chg_info.online_chg)
+               queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
+
+       /*
+        * Run the charging algorithm directly to be sure we don't
+        * do it too seldom
+        */
+       queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
+
+       return 0;
+}
+
+static int abx500_chargalg_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct abx500_chargalg *di = platform_get_drvdata(pdev);
+
+       if (di->chg_info.online_chg)
+               cancel_delayed_work_sync(&di->chargalg_wd_work);
+
+       cancel_delayed_work_sync(&di->chargalg_periodic_work);
+
+       return 0;
+}
+#else
+#define abx500_chargalg_suspend      NULL
+#define abx500_chargalg_resume       NULL
+#endif
+
+static int __devexit abx500_chargalg_remove(struct platform_device *pdev)
+{
+       struct abx500_chargalg *di = platform_get_drvdata(pdev);
+
+       /* sysfs interface to enable/disbale charging from user space */
+       abx500_chargalg_sysfs_exit(di);
+
+       /* Delete the work queue */
+       destroy_workqueue(di->chargalg_wq);
+
+       flush_scheduled_work();
+       power_supply_unregister(&di->chargalg_psy);
+       platform_set_drvdata(pdev, NULL);
+       kfree(di);
+
+       return 0;
+}
+
+static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
+{
+       struct abx500_bm_plat_data *plat_data;
+       int ret = 0;
+
+       struct abx500_chargalg *di =
+               kzalloc(sizeof(struct abx500_chargalg), GFP_KERNEL);
+       if (!di)
+               return -ENOMEM;
+
+       /* get device struct */
+       di->dev = &pdev->dev;
+
+       plat_data = pdev->dev.platform_data;
+       di->pdata = plat_data->chargalg;
+       di->bat = plat_data->battery;
+
+       /* chargalg supply */
+       di->chargalg_psy.name = "abx500_chargalg";
+       di->chargalg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+       di->chargalg_psy.properties = abx500_chargalg_props;
+       di->chargalg_psy.num_properties = ARRAY_SIZE(abx500_chargalg_props);
+       di->chargalg_psy.get_property = abx500_chargalg_get_property;
+       di->chargalg_psy.supplied_to = di->pdata->supplied_to;
+       di->chargalg_psy.num_supplicants = di->pdata->num_supplicants;
+       di->chargalg_psy.external_power_changed =
+               abx500_chargalg_external_power_changed;
+
+       /* Initilialize safety timer */
+       init_timer(&di->safety_timer);
+       di->safety_timer.function = abx500_chargalg_safety_timer_expired;
+       di->safety_timer.data = (unsigned long) di;
+
+       /* Initilialize maintenance timer */
+       init_timer(&di->maintenance_timer);
+       di->maintenance_timer.function =
+               abx500_chargalg_maintenance_timer_expired;
+       di->maintenance_timer.data = (unsigned long) di;
+
+       /* Create a work queue for the chargalg */
+       di->chargalg_wq =
+               create_singlethread_workqueue("abx500_chargalg_wq");
+       if (di->chargalg_wq == NULL) {
+               dev_err(di->dev, "failed to create work queue\n");
+               goto free_device_info;
+       }
+
+       /* Init work for chargalg */
+       INIT_DELAYED_WORK_DEFERRABLE(&di->chargalg_periodic_work,
+               abx500_chargalg_periodic_work);
+       INIT_DELAYED_WORK_DEFERRABLE(&di->chargalg_wd_work,
+               abx500_chargalg_wd_work);
+
+       /* Init work for chargalg */
+       INIT_WORK(&di->chargalg_work, abx500_chargalg_work);
+
+       /* To detect charger at startup */
+       di->chg_info.prev_conn_chg = -1;
+
+       /* Register chargalg power supply class */
+       ret = power_supply_register(di->dev, &di->chargalg_psy);
+       if (ret) {
+               dev_err(di->dev, "failed to register chargalg psy\n");
+               goto free_chargalg_wq;
+       }
+
+       platform_set_drvdata(pdev, di);
+
+       /* sysfs interface to enable/disable charging from user space */
+       ret = abx500_chargalg_sysfs_init(di);
+       if (ret) {
+               dev_err(di->dev, "failed to create sysfs entry\n");
+               goto free_psy;
+       }
+
+       /* Run the charging algorithm */
+       queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
+
+       dev_info(di->dev, "probe success\n");
+       return ret;
+
+free_psy:
+       power_supply_unregister(&di->chargalg_psy);
+free_chargalg_wq:
+       destroy_workqueue(di->chargalg_wq);
+free_device_info:
+       kfree(di);
+
+       return ret;
+}
+
+static struct platform_driver abx500_chargalg_driver = {
+       .probe = abx500_chargalg_probe,
+       .remove = __devexit_p(abx500_chargalg_remove),
+       .suspend = abx500_chargalg_suspend,
+       .resume = abx500_chargalg_resume,
+       .driver = {
+               .name = "abx500-chargalg",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init abx500_chargalg_init(void)
+{
+       return platform_driver_register(&abx500_chargalg_driver);
+}
+
+static void __exit abx500_chargalg_exit(void)
+{
+       platform_driver_unregister(&abx500_chargalg_driver);
+}
+
+module_init(abx500_chargalg_init);
+module_exit(abx500_chargalg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:abx500-chargalg");
+MODULE_DESCRIPTION("abx500 battery charging algorithm");
index 88fd9710bda212c22b59da04f970977e408ea6f2..9eca9f1ff0eae2e5b381e503f035ceb446340e66 100644 (file)
@@ -134,12 +134,11 @@ static int get_batt_uV(struct charger_manager *cm, int *uV)
        union power_supply_propval val;
        int ret;
 
-       if (cm->fuel_gauge)
-               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
-                               POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
-       else
+       if (!cm->fuel_gauge)
                return -ENODEV;
 
+       ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+                               POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
        if (ret)
                return ret;
 
@@ -245,9 +244,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
        struct charger_desc *desc = cm->desc;
 
        /* Ignore if it's redundent command */
-       if (enable && cm->charger_enabled)
-               return 0;
-       if (!enable && !cm->charger_enabled)
+       if (enable == cm->charger_enabled)
                return 0;
 
        if (enable) {
@@ -309,9 +306,7 @@ static void uevent_notify(struct charger_manager *cm, const char *event)
 
                if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
                        return; /* Duplicated. */
-               else
-                       strncpy(env_str_save, event, UEVENT_BUF_SIZE);
-
+               strncpy(env_str_save, event, UEVENT_BUF_SIZE);
                return;
        }
 
@@ -387,8 +382,10 @@ static bool cm_monitor(void)
 
        mutex_lock(&cm_list_mtx);
 
-       list_for_each_entry(cm, &cm_list, entry)
-               stop = stop || _cm_monitor(cm);
+       list_for_each_entry(cm, &cm_list, entry) {
+               if (_cm_monitor(cm))
+                       stop = true;
+       }
 
        mutex_unlock(&cm_list_mtx);
 
@@ -402,7 +399,8 @@ static int charger_get_property(struct power_supply *psy,
        struct charger_manager *cm = container_of(psy,
                        struct charger_manager, charger_psy);
        struct charger_desc *desc = cm->desc;
-       int i, ret = 0, uV;
+       int ret = 0;
+       int uV;
 
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
@@ -428,8 +426,7 @@ static int charger_get_property(struct power_supply *psy,
                        val->intval = 0;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = get_batt_uV(cm, &i);
-               val->intval = i;
+               ret = get_batt_uV(cm, &val->intval);
                break;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
                ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
@@ -697,8 +694,10 @@ bool cm_suspend_again(void)
        mutex_lock(&cm_list_mtx);
        list_for_each_entry(cm, &cm_list, entry) {
                if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
-                   cm->status_save_batt != is_batt_present(cm))
+                   cm->status_save_batt != is_batt_present(cm)) {
                        ret = false;
+                       break;
+               }
        }
        mutex_unlock(&cm_list_mtx);
 
@@ -855,11 +854,10 @@ static int charger_manager_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, cm);
 
-       memcpy(&cm->charger_psy, &psy_default,
-                               sizeof(psy_default));
+       memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default));
+
        if (!desc->psy_name) {
-               strncpy(cm->psy_name_buf, psy_default.name,
-                               PSY_NAME_MAX);
+               strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
        } else {
                strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
        }
@@ -894,15 +892,15 @@ static int charger_manager_probe(struct platform_device *pdev)
                                POWER_SUPPLY_PROP_CURRENT_NOW;
                cm->charger_psy.num_properties++;
        }
-       if (!desc->measure_battery_temp) {
-               cm->charger_psy.properties[cm->charger_psy.num_properties] =
-                               POWER_SUPPLY_PROP_TEMP_AMBIENT;
-               cm->charger_psy.num_properties++;
-       }
+
        if (desc->measure_battery_temp) {
                cm->charger_psy.properties[cm->charger_psy.num_properties] =
                                POWER_SUPPLY_PROP_TEMP;
                cm->charger_psy.num_properties++;
+       } else {
+               cm->charger_psy.properties[cm->charger_psy.num_properties] =
+                               POWER_SUPPLY_PROP_TEMP_AMBIENT;
+               cm->charger_psy.num_properties++;
        }
 
        ret = power_supply_register(NULL, &cm->charger_psy);
@@ -933,9 +931,8 @@ static int charger_manager_probe(struct platform_device *pdev)
        return 0;
 
 err_chg_enable:
-       if (desc->charger_regulators)
-               regulator_bulk_free(desc->num_charger_regulators,
-                                       desc->charger_regulators);
+       regulator_bulk_free(desc->num_charger_regulators,
+                           desc->charger_regulators);
 err_bulk_get:
        power_supply_unregister(&cm->charger_psy);
 err_register:
@@ -961,10 +958,8 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
        list_del(&cm->entry);
        mutex_unlock(&cm_list_mtx);
 
-       if (desc->charger_regulators)
-               regulator_bulk_free(desc->num_charger_regulators,
-                                       desc->charger_regulators);
-
+       regulator_bulk_free(desc->num_charger_regulators,
+                           desc->charger_regulators);
        power_supply_unregister(&cm->charger_psy);
        kfree(cm->charger_psy.properties);
        kfree(cm->charger_stat);
@@ -982,9 +977,7 @@ MODULE_DEVICE_TABLE(platform, charger_manager_id);
 
 static int cm_suspend_prepare(struct device *dev)
 {
-       struct platform_device *pdev = container_of(dev, struct platform_device,
-                                                   dev);
-       struct charger_manager *cm = platform_get_drvdata(pdev);
+       struct charger_manager *cm = dev_get_drvdata(dev);
 
        if (!cm_suspended) {
                if (rtc_dev) {
@@ -1020,9 +1013,7 @@ static int cm_suspend_prepare(struct device *dev)
 
 static void cm_suspend_complete(struct device *dev)
 {
-       struct platform_device *pdev = container_of(dev, struct platform_device,
-                                                   dev);
-       struct charger_manager *cm = platform_get_drvdata(pdev);
+       struct charger_manager *cm = dev_get_drvdata(dev);
 
        if (cm_suspended) {
                if (rtc_dev) {
index e8ea47a53dee53e2c29314038cdbf9ff9f0afc3f..a5f6a0ec15726b8e93180ea51b1a4facde4474a5 100644 (file)
@@ -612,6 +612,7 @@ static s32 __devinit da9052_bat_probe(struct platform_device *pdev)
         if (ret)
                goto err;
 
+       platform_set_drvdata(pdev, bat);
        return 0;
 
 err:
@@ -633,6 +634,7 @@ static int __devexit da9052_bat_remove(struct platform_device *pdev)
                free_irq(bat->da9052->irq_base + irq, bat);
        }
        power_supply_unregister(&bat->psy);
+       kfree(bat);
 
        return 0;
 }
@@ -645,18 +647,7 @@ static struct platform_driver da9052_bat_driver = {
                .owner = THIS_MODULE,
        },
 };
-
-static int __init da9052_bat_init(void)
-{
-       return platform_driver_register(&da9052_bat_driver);
-}
-module_init(da9052_bat_init);
-
-static void __exit da9052_bat_exit(void)
-{
-       platform_driver_unregister(&da9052_bat_driver);
-}
-module_exit(da9052_bat_exit);
+module_platform_driver(da9052_bat_driver);
 
 MODULE_DESCRIPTION("DA9052 BAT Device Driver");
 MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
index bfbce5de49daeeccfc3b8cf9daa585dbbd1076e4..6bb6e2f5ea814e408c52f23ca78d35f8a22e7444 100644 (file)
@@ -403,18 +403,7 @@ static struct i2c_driver ds278x_battery_driver = {
        .remove         = ds278x_battery_remove,
        .id_table       = ds278x_id,
 };
-
-static int __init ds278x_init(void)
-{
-       return i2c_add_driver(&ds278x_battery_driver);
-}
-module_init(ds278x_init);
-
-static void __exit ds278x_exit(void)
-{
-       i2c_del_driver(&ds278x_battery_driver);
-}
-module_exit(ds278x_exit);
+module_i2c_driver(ds278x_battery_driver);
 
 MODULE_AUTHOR("Ryan Mallon");
 MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
index 1289a5f790a12143a837c1ddcefd6a713610bcd6..39eb50f35f09fd777445a53202fb30823d9f9438 100644 (file)
@@ -480,6 +480,7 @@ fail0:
 
        dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
 
+       isp1704_charger_set_power(isp, 0);
        return ret;
 }
 
index c53dd1292f818bc595a54c863c63f362e28ad5aa..d8b75780bfef15718e9f578618381eeb4bc69560 100644 (file)
@@ -1,6 +1,7 @@
 /*
- * Driver for LP8727 Micro/Mini USB IC with intergrated charger
+ * Driver for LP8727 Micro/Mini USB IC with integrated charger
  *
+ *                     Copyright (C) 2011 Texas Instruments
  *                     Copyright (C) 2011 National Semiconductor
  *
  * This program is free software; you can redistribute it and/or modify
@@ -25,7 +26,7 @@
 #define INT1           0x4
 #define INT2           0x5
 #define STATUS1                0x6
-#define STATUS2        0x7
+#define STATUS2                0x7
 #define CHGCTRL2       0x9
 
 /* CTRL1 register */
@@ -91,7 +92,7 @@ struct lp8727_chg {
        enum lp8727_dev_id devid;
 };
 
-static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
+static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
 {
        s32 ret;
 
@@ -102,29 +103,22 @@ static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
        return (ret != len) ? -EIO : 0;
 }
 
-static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
+static inline int lp8727_read_byte(struct lp8727_chg *pchg, u8 reg, u8 *data)
 {
-       s32 ret;
+       return lp8727_read_bytes(pchg, reg, data, 1);
+}
+
+static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data)
+{
+       int ret;
 
        mutex_lock(&pchg->xfer_lock);
-       ret = i2c_smbus_write_i2c_block_data(pchg->client, reg, len, data);
+       ret = i2c_smbus_write_byte_data(pchg->client, reg, data);
        mutex_unlock(&pchg->xfer_lock);
 
        return ret;
 }
 
-static inline int lp8727_i2c_read_byte(struct lp8727_chg *pchg, u8 reg,
-                                      u8 *data)
-{
-       return lp8727_i2c_read(pchg, reg, data, 1);
-}
-
-static inline int lp8727_i2c_write_byte(struct lp8727_chg *pchg, u8 reg,
-                                       u8 *data)
-{
-       return lp8727_i2c_write(pchg, reg, data, 1);
-}
-
 static int lp8727_is_charger_attached(const char *name, int id)
 {
        if (name) {
@@ -137,37 +131,41 @@ static int lp8727_is_charger_attached(const char *name, int id)
        return (id >= ID_TA && id <= ID_USB_CHG) ? 1 : 0;
 }
 
-static void lp8727_init_device(struct lp8727_chg *pchg)
+static int lp8727_init_device(struct lp8727_chg *pchg)
 {
        u8 val;
+       int ret;
 
        val = ID200_EN | ADC_EN | CP_EN;
-       if (lp8727_i2c_write_byte(pchg, CTRL1, &val))
-               dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL1);
+       ret = lp8727_write_byte(pchg, CTRL1, val);
+       if (ret)
+               return ret;
 
        val = INT_EN | CHGDET_EN;
-       if (lp8727_i2c_write_byte(pchg, CTRL2, &val))
-               dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL2);
+       ret = lp8727_write_byte(pchg, CTRL2, val);
+       if (ret)
+               return ret;
+
+       return 0;
 }
 
 static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
 {
        u8 val;
-       lp8727_i2c_read_byte(pchg, STATUS1, &val);
-       return (val & DCPORT);
+       lp8727_read_byte(pchg, STATUS1, &val);
+       return val & DCPORT;
 }
 
 static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
 {
        u8 val;
-       lp8727_i2c_read_byte(pchg, STATUS1, &val);
-       return (val & CHPORT);
+       lp8727_read_byte(pchg, STATUS1, &val);
+       return val & CHPORT;
 }
 
 static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
 {
-       u8 val = sw;
-       lp8727_i2c_write_byte(pchg, SWCTRL, &val);
+       lp8727_write_byte(pchg, SWCTRL, sw);
 }
 
 static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
@@ -207,9 +205,9 @@ static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
 {
        u8 val;
 
-       lp8727_i2c_read_byte(pchg, CTRL2, &val);
+       lp8727_read_byte(pchg, CTRL2, &val);
        val |= CHGDET_EN;
-       lp8727_i2c_write_byte(pchg, CTRL2, &val);
+       lp8727_write_byte(pchg, CTRL2, val);
 }
 
 static void lp8727_delayed_func(struct work_struct *_work)
@@ -218,7 +216,7 @@ static void lp8727_delayed_func(struct work_struct *_work)
        struct lp8727_chg *pchg =
            container_of(_work, struct lp8727_chg, work.work);
 
-       if (lp8727_i2c_read(pchg, INT1, intstat, 2)) {
+       if (lp8727_read_bytes(pchg, INT1, intstat, 2)) {
                dev_err(pchg->dev, "can not read INT registers\n");
                return;
        }
@@ -244,20 +242,22 @@ static irqreturn_t lp8727_isr_func(int irq, void *ptr)
        return IRQ_HANDLED;
 }
 
-static void lp8727_intr_config(struct lp8727_chg *pchg)
+static int lp8727_intr_config(struct lp8727_chg *pchg)
 {
        INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
 
        pchg->irqthread = create_singlethread_workqueue("lp8727-irqthd");
-       if (!pchg->irqthread)
+       if (!pchg->irqthread) {
                dev_err(pchg->dev, "can not create thread for lp8727\n");
-
-       if (request_threaded_irq(pchg->client->irq,
-                                NULL,
-                                lp8727_isr_func,
-                                IRQF_TRIGGER_FALLING, "lp8727_irq", pchg)) {
-               dev_err(pchg->dev, "lp8727 irq can not be registered\n");
+               return -ENOMEM;
        }
+
+       return request_threaded_irq(pchg->client->irq,
+                               NULL,
+                               lp8727_isr_func,
+                               IRQF_TRIGGER_FALLING,
+                               "lp8727_irq",
+                               pchg);
 }
 
 static enum power_supply_property lp8727_charger_prop[] = {
@@ -300,7 +300,7 @@ static int lp8727_battery_get_property(struct power_supply *psy,
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
                if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
-                       lp8727_i2c_read_byte(pchg, STATUS1, &read);
+                       lp8727_read_byte(pchg, STATUS1, &read);
                        if (((read & CHGSTAT) >> 4) == EOC)
                                val->intval = POWER_SUPPLY_STATUS_FULL;
                        else
@@ -310,7 +310,7 @@ static int lp8727_battery_get_property(struct power_supply *psy,
                }
                break;
        case POWER_SUPPLY_PROP_HEALTH:
-               lp8727_i2c_read_byte(pchg, STATUS2, &read);
+               lp8727_read_byte(pchg, STATUS2, &read);
                read = (read & TEMP_STAT) >> 5;
                if (read >= 0x1 && read <= 0x3)
                        val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
@@ -351,7 +351,7 @@ static void lp8727_charger_changed(struct power_supply *psy)
                        eoc_level = pchg->chg_parm->eoc_level;
                        ichg = pchg->chg_parm->ichg;
                        val = (ichg << 4) | eoc_level;
-                       lp8727_i2c_write_byte(pchg, CHGCTRL2, &val);
+                       lp8727_write_byte(pchg, CHGCTRL2, val);
                }
        }
 }
@@ -439,15 +439,29 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
 
        mutex_init(&pchg->xfer_lock);
 
-       lp8727_init_device(pchg);
-       lp8727_intr_config(pchg);
+       ret = lp8727_init_device(pchg);
+       if (ret) {
+               dev_err(pchg->dev, "i2c communication err: %d", ret);
+               goto error;
+       }
+
+       ret = lp8727_intr_config(pchg);
+       if (ret) {
+               dev_err(pchg->dev, "irq handler err: %d", ret);
+               goto error;
+       }
 
        ret = lp8727_register_psy(pchg);
-       if (ret)
-               dev_err(pchg->dev,
-                       "can not register power supplies. err=%d", ret);
+       if (ret) {
+               dev_err(pchg->dev, "power supplies register err: %d", ret);
+               goto error;
+       }
 
        return 0;
+
+error:
+       kfree(pchg);
+       return ret;
 }
 
 static int __devexit lp8727_remove(struct i2c_client *cl)
@@ -466,6 +480,7 @@ static const struct i2c_device_id lp8727_ids[] = {
        {"lp8727", 0},
        { }
 };
+MODULE_DEVICE_TABLE(i2c, lp8727_ids);
 
 static struct i2c_driver lp8727_driver = {
        .driver = {
@@ -475,21 +490,9 @@ static struct i2c_driver lp8727_driver = {
        .remove = __devexit_p(lp8727_remove),
        .id_table = lp8727_ids,
 };
+module_i2c_driver(lp8727_driver);
 
-static int __init lp8727_init(void)
-{
-       return i2c_add_driver(&lp8727_driver);
-}
-
-static void __exit lp8727_exit(void)
-{
-       i2c_del_driver(&lp8727_driver);
-}
-
-module_init(lp8727_init);
-module_exit(lp8727_exit);
-
-MODULE_DESCRIPTION("National Semiconductor LP8727 charger driver");
-MODULE_AUTHOR
-    ("Woogyom Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");
+MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver");
+MODULE_AUTHOR("Woogyom Kim <milo.kim@ti.com>, "
+             "Daniel Jeong <daniel.jeong@ti.com>");
 MODULE_LICENSE("GPL");
index 2f2f9a6f54fad6d75dc692fbee4d128b7ba642a4..c284143cfcd76ba7c3ccd3245fb379f953e0f40f 100644 (file)
@@ -290,18 +290,7 @@ static struct i2c_driver max17040_i2c_driver = {
        .resume         = max17040_resume,
        .id_table       = max17040_id,
 };
-
-static int __init max17040_init(void)
-{
-       return i2c_add_driver(&max17040_i2c_driver);
-}
-module_init(max17040_init);
-
-static void __exit max17040_exit(void)
-{
-       i2c_del_driver(&max17040_i2c_driver);
-}
-module_exit(max17040_exit);
+module_i2c_driver(max17040_i2c_driver);
 
 MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
 MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
index 86acee2f988917912919e710d4b62f1592b98397..04620c2cb388f3f5f2411f6a2b8191c5b36b459f 100644 (file)
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
 #include <linux/mod_devicetable.h>
 #include <linux/power_supply.h>
 #include <linux/power/max17042_battery.h>
+#include <linux/of.h>
+
+/* Status register bits */
+#define STATUS_POR_BIT         (1 << 1)
+#define STATUS_BST_BIT         (1 << 3)
+#define STATUS_VMN_BIT         (1 << 8)
+#define STATUS_TMN_BIT         (1 << 9)
+#define STATUS_SMN_BIT         (1 << 10)
+#define STATUS_BI_BIT          (1 << 11)
+#define STATUS_VMX_BIT         (1 << 12)
+#define STATUS_TMX_BIT         (1 << 13)
+#define STATUS_SMX_BIT         (1 << 14)
+#define STATUS_BR_BIT          (1 << 15)
+
+/* Interrupt mask bits */
+#define CONFIG_ALRT_BIT_ENBL   (1 << 2)
+#define STATUS_INTR_SOCMIN_BIT (1 << 10)
+#define STATUS_INTR_SOCMAX_BIT (1 << 14)
+
+#define VFSOC0_LOCK            0x0000
+#define VFSOC0_UNLOCK          0x0080
+#define MODEL_UNLOCK1  0X0059
+#define MODEL_UNLOCK2  0X00C4
+#define MODEL_LOCK1            0X0000
+#define MODEL_LOCK2            0X0000
+
+#define dQ_ACC_DIV     0x4
+#define dP_ACC_100     0x1900
+#define dP_ACC_200     0x3200
 
 struct max17042_chip {
        struct i2c_client *client;
        struct power_supply battery;
        struct max17042_platform_data *pdata;
+       struct work_struct work;
+       int    init_complete;
 };
 
 static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value)
@@ -87,6 +120,9 @@ static int max17042_get_property(struct power_supply *psy,
                                struct max17042_chip, battery);
        int ret;
 
+       if (!chip->init_complete)
+               return -EAGAIN;
+
        switch (psp) {
        case POWER_SUPPLY_PROP_PRESENT:
                ret = max17042_read_reg(chip->client, MAX17042_STATUS);
@@ -136,21 +172,18 @@ static int max17042_get_property(struct power_supply *psy,
                val->intval = ret * 625 / 8;
                break;
        case POWER_SUPPLY_PROP_CAPACITY:
-               ret = max17042_read_reg(chip->client, MAX17042_SOC);
+               ret = max17042_read_reg(chip->client, MAX17042_RepSOC);
                if (ret < 0)
                        return ret;
 
                val->intval = ret >> 8;
                break;
        case POWER_SUPPLY_PROP_CHARGE_FULL:
-               ret = max17042_read_reg(chip->client, MAX17042_RepSOC);
+               ret = max17042_read_reg(chip->client, MAX17042_FullCAP);
                if (ret < 0)
                        return ret;
 
-               if ((ret >> 8) >= MAX17042_BATTERY_FULL)
-                       val->intval = 1;
-               else if (ret >= 0)
-                       val->intval = 0;
+               val->intval = ret * 1000 / 2;
                break;
        case POWER_SUPPLY_PROP_TEMP:
                ret = max17042_read_reg(chip->client, MAX17042_TEMP);
@@ -210,22 +243,419 @@ static int max17042_get_property(struct power_supply *psy,
        return 0;
 }
 
+static int max17042_write_verify_reg(struct i2c_client *client,
+                               u8 reg, u16 value)
+{
+       int retries = 8;
+       int ret;
+       u16 read_value;
+
+       do {
+               ret = i2c_smbus_write_word_data(client, reg, value);
+               read_value =  max17042_read_reg(client, reg);
+               if (read_value != value) {
+                       ret = -EIO;
+                       retries--;
+               }
+       } while (retries && read_value != value);
+
+       if (ret < 0)
+               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+       return ret;
+}
+
+static inline void max17042_override_por(
+       struct i2c_client *client, u8 reg, u16 value)
+{
+       if (value)
+               max17042_write_reg(client, reg, value);
+}
+
+static inline void max10742_unlock_model(struct max17042_chip *chip)
+{
+       struct i2c_client *client = chip->client;
+       max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
+       max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
+}
+
+static inline void max10742_lock_model(struct max17042_chip *chip)
+{
+       struct i2c_client *client = chip->client;
+       max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_LOCK1);
+       max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_LOCK2);
+}
+
+static inline void max17042_write_model_data(struct max17042_chip *chip,
+                                       u8 addr, int size)
+{
+       struct i2c_client *client = chip->client;
+       int i;
+       for (i = 0; i < size; i++)
+               max17042_write_reg(client, addr + i,
+                               chip->pdata->config_data->cell_char_tbl[i]);
+}
+
+static inline void max17042_read_model_data(struct max17042_chip *chip,
+                                       u8 addr, u16 *data, int size)
+{
+       struct i2c_client *client = chip->client;
+       int i;
+
+       for (i = 0; i < size; i++)
+               data[i] = max17042_read_reg(client, addr + i);
+}
+
+static inline int max17042_model_data_compare(struct max17042_chip *chip,
+                                       u16 *data1, u16 *data2, int size)
+{
+       int i;
+
+       if (memcmp(data1, data2, size)) {
+               dev_err(&chip->client->dev, "%s compare failed\n", __func__);
+               for (i = 0; i < size; i++)
+                       dev_info(&chip->client->dev, "0x%x, 0x%x",
+                               data1[i], data2[i]);
+               dev_info(&chip->client->dev, "\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int max17042_init_model(struct max17042_chip *chip)
+{
+       int ret;
+       int table_size =
+               sizeof(chip->pdata->config_data->cell_char_tbl)/sizeof(u16);
+       u16 *temp_data;
+
+       temp_data = kzalloc(table_size, GFP_KERNEL);
+       if (!temp_data)
+               return -ENOMEM;
+
+       max10742_unlock_model(chip);
+       max17042_write_model_data(chip, MAX17042_MODELChrTbl,
+                               table_size);
+       max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
+                               table_size);
+
+       ret = max17042_model_data_compare(
+               chip,
+               chip->pdata->config_data->cell_char_tbl,
+               temp_data,
+               table_size);
+
+       max10742_lock_model(chip);
+       kfree(temp_data);
+
+       return ret;
+}
+
+static int max17042_verify_model_lock(struct max17042_chip *chip)
+{
+       int i;
+       int table_size =
+               sizeof(chip->pdata->config_data->cell_char_tbl);
+       u16 *temp_data;
+       int ret = 0;
+
+       temp_data = kzalloc(table_size, GFP_KERNEL);
+       if (!temp_data)
+               return -ENOMEM;
+
+       max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
+                               table_size);
+       for (i = 0; i < table_size; i++)
+               if (temp_data[i])
+                       ret = -EINVAL;
+
+       kfree(temp_data);
+       return ret;
+}
+
+static void max17042_write_config_regs(struct max17042_chip *chip)
+{
+       struct max17042_config_data *config = chip->pdata->config_data;
+
+       max17042_write_reg(chip->client, MAX17042_CONFIG, config->config);
+       max17042_write_reg(chip->client, MAX17042_LearnCFG, config->learn_cfg);
+       max17042_write_reg(chip->client, MAX17042_FilterCFG,
+                       config->filter_cfg);
+       max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg);
+}
+
+static void  max17042_write_custom_regs(struct max17042_chip *chip)
+{
+       struct max17042_config_data *config = chip->pdata->config_data;
+
+       max17042_write_verify_reg(chip->client, MAX17042_RCOMP0,
+                               config->rcomp0);
+       max17042_write_verify_reg(chip->client, MAX17042_TempCo,
+                               config->tcompc0);
+       max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
+                       config->empty_tempco);
+       max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
+                               config->kempty0);
+       max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm,
+                               config->ichgt_term);
+}
+
+static void max17042_update_capacity_regs(struct max17042_chip *chip)
+{
+       struct max17042_config_data *config = chip->pdata->config_data;
+
+       max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
+                               config->fullcap);
+       max17042_write_reg(chip->client, MAX17042_DesignCap,
+                       config->design_cap);
+       max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
+                               config->fullcapnom);
+}
+
+static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)
+{
+       u16 vfSoc;
+
+       vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
+       max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
+       max17042_write_verify_reg(chip->client, MAX17042_VFSOC0, vfSoc);
+       max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
+}
+
+static void max17042_load_new_capacity_params(struct max17042_chip *chip)
+{
+       u16 full_cap0, rep_cap, dq_acc, vfSoc;
+       u32 rem_cap;
+
+       struct max17042_config_data *config = chip->pdata->config_data;
+
+       full_cap0 = max17042_read_reg(chip->client, MAX17042_FullCAP0);
+       vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
+
+       /* fg_vfSoc needs to shifted by 8 bits to get the
+        * perc in 1% accuracy, to get the right rem_cap multiply
+        * full_cap0, fg_vfSoc and devide by 100
+        */
+       rem_cap = ((vfSoc >> 8) * full_cap0) / 100;
+       max17042_write_verify_reg(chip->client, MAX17042_RemCap, (u16)rem_cap);
+
+       rep_cap = (u16)rem_cap;
+       max17042_write_verify_reg(chip->client, MAX17042_RepCap, rep_cap);
+
+       /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */
+       dq_acc = config->fullcap / dQ_ACC_DIV;
+       max17042_write_verify_reg(chip->client, MAX17042_dQacc, dq_acc);
+       max17042_write_verify_reg(chip->client, MAX17042_dPacc, dP_ACC_200);
+
+       max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
+                       config->fullcap);
+       max17042_write_reg(chip->client, MAX17042_DesignCap,
+                       config->design_cap);
+       max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
+                       config->fullcapnom);
+}
+
+/*
+ * Block write all the override values coming from platform data.
+ * This function MUST be called before the POR initialization proceedure
+ * specified by maxim.
+ */
+static inline void max17042_override_por_values(struct max17042_chip *chip)
+{
+       struct i2c_client *client = chip->client;
+       struct max17042_config_data *config = chip->pdata->config_data;
+
+       max17042_override_por(client, MAX17042_TGAIN, config->tgain);
+       max17042_override_por(client, MAx17042_TOFF, config->toff);
+       max17042_override_por(client, MAX17042_CGAIN, config->cgain);
+       max17042_override_por(client, MAX17042_COFF, config->coff);
+
+       max17042_override_por(client, MAX17042_VALRT_Th, config->valrt_thresh);
+       max17042_override_por(client, MAX17042_TALRT_Th, config->talrt_thresh);
+       max17042_override_por(client, MAX17042_SALRT_Th,
+                       config->soc_alrt_thresh);
+       max17042_override_por(client, MAX17042_CONFIG, config->config);
+       max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer);
+
+       max17042_override_por(client, MAX17042_DesignCap, config->design_cap);
+       max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term);
+
+       max17042_override_por(client, MAX17042_AtRate, config->at_rate);
+       max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg);
+       max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg);
+       max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg);
+       max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg);
+       max17042_override_por(client, MAX17042_MaskSOC, config->masksoc);
+
+       max17042_override_por(client, MAX17042_FullCAP, config->fullcap);
+       max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom);
+       max17042_override_por(client, MAX17042_SOC_empty, config->socempty);
+       max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty);
+       max17042_override_por(client, MAX17042_dQacc, config->dqacc);
+       max17042_override_por(client, MAX17042_dPacc, config->dpacc);
+
+       max17042_override_por(client, MAX17042_V_empty, config->vempty);
+       max17042_override_por(client, MAX17042_TempNom, config->temp_nom);
+       max17042_override_por(client, MAX17042_TempLim, config->temp_lim);
+       max17042_override_por(client, MAX17042_FCTC, config->fctc);
+       max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0);
+       max17042_override_por(client, MAX17042_TempCo, config->tcompc0);
+       max17042_override_por(client, MAX17042_EmptyTempCo,
+                       config->empty_tempco);
+       max17042_override_por(client, MAX17042_K_empty0, config->kempty0);
+}
+
+static int max17042_init_chip(struct max17042_chip *chip)
+{
+       int ret;
+       int val;
+
+       max17042_override_por_values(chip);
+       /* After Power up, the MAX17042 requires 500mS in order
+        * to perform signal debouncing and initial SOC reporting
+        */
+       msleep(500);
+
+       /* Initialize configaration */
+       max17042_write_config_regs(chip);
+
+       /* write cell characterization data */
+       ret = max17042_init_model(chip);
+       if (ret) {
+               dev_err(&chip->client->dev, "%s init failed\n",
+                       __func__);
+               return -EIO;
+       }
+       max17042_verify_model_lock(chip);
+       if (ret) {
+               dev_err(&chip->client->dev, "%s lock verify failed\n",
+                       __func__);
+               return -EIO;
+       }
+       /* write custom parameters */
+       max17042_write_custom_regs(chip);
+
+       /* update capacity params */
+       max17042_update_capacity_regs(chip);
+
+       /* delay must be atleast 350mS to allow VFSOC
+        * to be calculated from the new configuration
+        */
+       msleep(350);
+
+       /* reset vfsoc0 reg */
+       max17042_reset_vfsoc0_reg(chip);
+
+       /* load new capacity params */
+       max17042_load_new_capacity_params(chip);
+
+       /* Init complete, Clear the POR bit */
+       val = max17042_read_reg(chip->client, MAX17042_STATUS);
+       max17042_write_reg(chip->client, MAX17042_STATUS,
+                       val & (~STATUS_POR_BIT));
+       return 0;
+}
+
+static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
+{
+       u16 soc, soc_tr;
+
+       /* program interrupt thesholds such that we should
+        * get interrupt for every 'off' perc change in the soc
+        */
+       soc = max17042_read_reg(chip->client, MAX17042_RepSOC) >> 8;
+       soc_tr = (soc + off) << 8;
+       soc_tr |= (soc - off);
+       max17042_write_reg(chip->client, MAX17042_SALRT_Th, soc_tr);
+}
+
+static irqreturn_t max17042_thread_handler(int id, void *dev)
+{
+       struct max17042_chip *chip = dev;
+       u16 val;
+
+       val = max17042_read_reg(chip->client, MAX17042_STATUS);
+       if ((val & STATUS_INTR_SOCMIN_BIT) ||
+               (val & STATUS_INTR_SOCMAX_BIT)) {
+               dev_info(&chip->client->dev, "SOC threshold INTR\n");
+               max17042_set_soc_threshold(chip, 1);
+       }
+
+       power_supply_changed(&chip->battery);
+       return IRQ_HANDLED;
+}
+
+static void max17042_init_worker(struct work_struct *work)
+{
+       struct max17042_chip *chip = container_of(work,
+                               struct max17042_chip, work);
+       int ret;
+
+       /* Initialize registers according to values from the platform data */
+       if (chip->pdata->enable_por_init && chip->pdata->config_data) {
+               ret = max17042_init_chip(chip);
+               if (ret)
+                       return;
+       }
+
+       chip->init_complete = 1;
+}
+
+#ifdef CONFIG_OF
+static struct max17042_platform_data *
+max17042_get_pdata(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       u32 prop;
+       struct max17042_platform_data *pdata;
+
+       if (!np)
+               return dev->platform_data;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       /*
+        * Require current sense resistor value to be specified for
+        * current-sense functionality to be enabled at all.
+        */
+       if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) {
+               pdata->r_sns = prop;
+               pdata->enable_current_sense = true;
+       }
+
+       return pdata;
+}
+#else
+static struct max17042_platform_data *
+max17042_get_pdata(struct device *dev)
+{
+       return dev->platform_data;
+}
+#endif
+
 static int __devinit max17042_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
        struct max17042_chip *chip;
        int ret;
+       int reg;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
                return -EIO;
 
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
        if (!chip)
                return -ENOMEM;
 
        chip->client = client;
-       chip->pdata = client->dev.platform_data;
+       chip->pdata = max17042_get_pdata(&client->dev);
+       if (!chip->pdata) {
+               dev_err(&client->dev, "no platform data provided\n");
+               return -EINVAL;
+       }
 
        i2c_set_clientdata(client, chip);
 
@@ -243,17 +673,9 @@ static int __devinit max17042_probe(struct i2c_client *client,
        if (chip->pdata->r_sns == 0)
                chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
 
-       ret = power_supply_register(&client->dev, &chip->battery);
-       if (ret) {
-               dev_err(&client->dev, "failed: power supply register\n");
-               kfree(chip);
-               return ret;
-       }
-
-       /* Initialize registers according to values from the platform data */
        if (chip->pdata->init_data)
                max17042_set_reg(client, chip->pdata->init_data,
-                                chip->pdata->num_init_data);
+                               chip->pdata->num_init_data);
 
        if (!chip->pdata->enable_current_sense) {
                max17042_write_reg(client, MAX17042_CGAIN, 0x0000);
@@ -261,7 +683,34 @@ static int __devinit max17042_probe(struct i2c_client *client,
                max17042_write_reg(client, MAX17042_LearnCFG, 0x0007);
        }
 
-       return 0;
+       if (client->irq) {
+               ret = request_threaded_irq(client->irq, NULL,
+                                               max17042_thread_handler,
+                                               IRQF_TRIGGER_FALLING,
+                                               chip->battery.name, chip);
+               if (!ret) {
+                       reg =  max17042_read_reg(client, MAX17042_CONFIG);
+                       reg |= CONFIG_ALRT_BIT_ENBL;
+                       max17042_write_reg(client, MAX17042_CONFIG, reg);
+                       max17042_set_soc_threshold(chip, 1);
+               } else
+                       dev_err(&client->dev, "%s(): cannot get IRQ\n",
+                               __func__);
+       }
+
+       reg = max17042_read_reg(chip->client, MAX17042_STATUS);
+
+       if (reg & STATUS_POR_BIT) {
+               INIT_WORK(&chip->work, max17042_init_worker);
+               schedule_work(&chip->work);
+       } else {
+               chip->init_complete = 1;
+       }
+
+       ret = power_supply_register(&client->dev, &chip->battery);
+       if (ret)
+               dev_err(&client->dev, "failed: power supply register\n");
+       return ret;
 }
 
 static int __devexit max17042_remove(struct i2c_client *client)
@@ -269,10 +718,17 @@ static int __devexit max17042_remove(struct i2c_client *client)
        struct max17042_chip *chip = i2c_get_clientdata(client);
 
        power_supply_unregister(&chip->battery);
-       kfree(chip);
        return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id max17042_dt_match[] = {
+       { .compatible = "maxim,max17042" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, max17042_dt_match);
+#endif
+
 static const struct i2c_device_id max17042_id[] = {
        { "max17042", 0 },
        { }
@@ -282,23 +738,13 @@ MODULE_DEVICE_TABLE(i2c, max17042_id);
 static struct i2c_driver max17042_i2c_driver = {
        .driver = {
                .name   = "max17042",
+               .of_match_table = of_match_ptr(max17042_dt_match),
        },
        .probe          = max17042_probe,
        .remove         = __devexit_p(max17042_remove),
        .id_table       = max17042_id,
 };
-
-static int __init max17042_init(void)
-{
-       return i2c_add_driver(&max17042_i2c_driver);
-}
-module_init(max17042_init);
-
-static void __exit max17042_exit(void)
-{
-       i2c_del_driver(&max17042_i2c_driver);
-}
-module_exit(max17042_exit);
+module_i2c_driver(max17042_i2c_driver);
 
 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
 MODULE_DESCRIPTION("MAX17042 Fuel Gauge");
index 9ff8af069da6f681830ccd7207e86d9163c62e3c..06b659d9179009e032bd7aaf2953aa8bf82c84fb 100644 (file)
@@ -852,18 +852,7 @@ static struct i2c_driver sbs_battery_driver = {
                .of_match_table = sbs_dt_ids,
        },
 };
-
-static int __init sbs_battery_init(void)
-{
-       return i2c_add_driver(&sbs_battery_driver);
-}
-module_init(sbs_battery_init);
-
-static void __exit sbs_battery_exit(void)
-{
-       i2c_del_driver(&sbs_battery_driver);
-}
-module_exit(sbs_battery_exit);
+module_i2c_driver(sbs_battery_driver);
 
 MODULE_DESCRIPTION("SBS battery monitor driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c
new file mode 100644 (file)
index 0000000..ce1694d
--- /dev/null
@@ -0,0 +1,1294 @@
+/*
+ * Summit Microelectronics SMB347 Battery Charger Driver
+ *
+ * Copyright (C) 2011, Intel Corporation
+ *
+ * Authors: Bruce E. Robertson <bruce.e.robertson@intel.com>
+ *          Mika Westerberg <mika.westerberg@linux.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.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/power/smb347-charger.h>
+#include <linux/seq_file.h>
+
+/*
+ * Configuration registers. These are mirrored to volatile RAM and can be
+ * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be
+ * reloaded from non-volatile registers after POR.
+ */
+#define CFG_CHARGE_CURRENT                     0x00
+#define CFG_CHARGE_CURRENT_FCC_MASK            0xe0
+#define CFG_CHARGE_CURRENT_FCC_SHIFT           5
+#define CFG_CHARGE_CURRENT_PCC_MASK            0x18
+#define CFG_CHARGE_CURRENT_PCC_SHIFT           3
+#define CFG_CHARGE_CURRENT_TC_MASK             0x07
+#define CFG_CURRENT_LIMIT                      0x01
+#define CFG_CURRENT_LIMIT_DC_MASK              0xf0
+#define CFG_CURRENT_LIMIT_DC_SHIFT             4
+#define CFG_CURRENT_LIMIT_USB_MASK             0x0f
+#define CFG_FLOAT_VOLTAGE                      0x03
+#define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK       0xc0
+#define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT      6
+#define CFG_STAT                               0x05
+#define CFG_STAT_DISABLED                      BIT(5)
+#define CFG_STAT_ACTIVE_HIGH                   BIT(7)
+#define CFG_PIN                                        0x06
+#define CFG_PIN_EN_CTRL_MASK                   0x60
+#define CFG_PIN_EN_CTRL_ACTIVE_HIGH            0x40
+#define CFG_PIN_EN_CTRL_ACTIVE_LOW             0x60
+#define CFG_PIN_EN_APSD_IRQ                    BIT(1)
+#define CFG_PIN_EN_CHARGER_ERROR               BIT(2)
+#define CFG_THERM                              0x07
+#define CFG_THERM_SOFT_HOT_COMPENSATION_MASK   0x03
+#define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT  0
+#define CFG_THERM_SOFT_COLD_COMPENSATION_MASK  0x0c
+#define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2
+#define CFG_THERM_MONITOR_DISABLED             BIT(4)
+#define CFG_SYSOK                              0x08
+#define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED  BIT(2)
+#define CFG_OTHER                              0x09
+#define CFG_OTHER_RID_MASK                     0xc0
+#define CFG_OTHER_RID_ENABLED_AUTO_OTG         0xc0
+#define CFG_OTG                                        0x0a
+#define CFG_OTG_TEMP_THRESHOLD_MASK            0x30
+#define CFG_OTG_TEMP_THRESHOLD_SHIFT           4
+#define CFG_OTG_CC_COMPENSATION_MASK           0xc0
+#define CFG_OTG_CC_COMPENSATION_SHIFT          6
+#define CFG_TEMP_LIMIT                         0x0b
+#define CFG_TEMP_LIMIT_SOFT_HOT_MASK           0x03
+#define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT          0
+#define CFG_TEMP_LIMIT_SOFT_COLD_MASK          0x0c
+#define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT         2
+#define CFG_TEMP_LIMIT_HARD_HOT_MASK           0x30
+#define CFG_TEMP_LIMIT_HARD_HOT_SHIFT          4
+#define CFG_TEMP_LIMIT_HARD_COLD_MASK          0xc0
+#define CFG_TEMP_LIMIT_HARD_COLD_SHIFT         6
+#define CFG_FAULT_IRQ                          0x0c
+#define CFG_FAULT_IRQ_DCIN_UV                  BIT(2)
+#define CFG_STATUS_IRQ                         0x0d
+#define CFG_STATUS_IRQ_TERMINATION_OR_TAPER    BIT(4)
+#define CFG_ADDRESS                            0x0e
+
+/* Command registers */
+#define CMD_A                                  0x30
+#define CMD_A_CHG_ENABLED                      BIT(1)
+#define CMD_A_SUSPEND_ENABLED                  BIT(2)
+#define CMD_A_ALLOW_WRITE                      BIT(7)
+#define CMD_B                                  0x31
+#define CMD_C                                  0x33
+
+/* Interrupt Status registers */
+#define IRQSTAT_A                              0x35
+#define IRQSTAT_C                              0x37
+#define IRQSTAT_C_TERMINATION_STAT             BIT(0)
+#define IRQSTAT_C_TERMINATION_IRQ              BIT(1)
+#define IRQSTAT_C_TAPER_IRQ                    BIT(3)
+#define IRQSTAT_E                              0x39
+#define IRQSTAT_E_USBIN_UV_STAT                        BIT(0)
+#define IRQSTAT_E_USBIN_UV_IRQ                 BIT(1)
+#define IRQSTAT_E_DCIN_UV_STAT                 BIT(4)
+#define IRQSTAT_E_DCIN_UV_IRQ                  BIT(5)
+#define IRQSTAT_F                              0x3a
+
+/* Status registers */
+#define STAT_A                                 0x3b
+#define STAT_A_FLOAT_VOLTAGE_MASK              0x3f
+#define STAT_B                                 0x3c
+#define STAT_C                                 0x3d
+#define STAT_C_CHG_ENABLED                     BIT(0)
+#define STAT_C_CHG_MASK                                0x06
+#define STAT_C_CHG_SHIFT                       1
+#define STAT_C_CHARGER_ERROR                   BIT(6)
+#define STAT_E                                 0x3f
+
+/**
+ * struct smb347_charger - smb347 charger instance
+ * @lock: protects concurrent access to online variables
+ * @client: pointer to i2c client
+ * @mains: power_supply instance for AC/DC power
+ * @usb: power_supply instance for USB power
+ * @battery: power_supply instance for battery
+ * @mains_online: is AC/DC input connected
+ * @usb_online: is USB input connected
+ * @charging_enabled: is charging enabled
+ * @dentry: for debugfs
+ * @pdata: pointer to platform data
+ */
+struct smb347_charger {
+       struct mutex            lock;
+       struct i2c_client       *client;
+       struct power_supply     mains;
+       struct power_supply     usb;
+       struct power_supply     battery;
+       bool                    mains_online;
+       bool                    usb_online;
+       bool                    charging_enabled;
+       struct dentry           *dentry;
+       const struct smb347_charger_platform_data *pdata;
+};
+
+/* Fast charge current in uA */
+static const unsigned int fcc_tbl[] = {
+       700000,
+       900000,
+       1200000,
+       1500000,
+       1800000,
+       2000000,
+       2200000,
+       2500000,
+};
+
+/* Pre-charge current in uA */
+static const unsigned int pcc_tbl[] = {
+       100000,
+       150000,
+       200000,
+       250000,
+};
+
+/* Termination current in uA */
+static const unsigned int tc_tbl[] = {
+       37500,
+       50000,
+       100000,
+       150000,
+       200000,
+       250000,
+       500000,
+       600000,
+};
+
+/* Input current limit in uA */
+static const unsigned int icl_tbl[] = {
+       300000,
+       500000,
+       700000,
+       900000,
+       1200000,
+       1500000,
+       1800000,
+       2000000,
+       2200000,
+       2500000,
+};
+
+/* Charge current compensation in uA */
+static const unsigned int ccc_tbl[] = {
+       250000,
+       700000,
+       900000,
+       1200000,
+};
+
+/* Convert register value to current using lookup table */
+static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val)
+{
+       if (val >= size)
+               return -EINVAL;
+       return tbl[val];
+}
+
+/* Convert current to register value using lookup table */
+static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
+{
+       size_t i;
+
+       for (i = 0; i < size; i++)
+               if (val < tbl[i])
+                       break;
+       return i > 0 ? i - 1 : -EINVAL;
+}
+
+static int smb347_read(struct smb347_charger *smb, u8 reg)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(smb->client, reg);
+       if (ret < 0)
+               dev_warn(&smb->client->dev, "failed to read reg 0x%x: %d\n",
+                        reg, ret);
+       return ret;
+}
+
+static int smb347_write(struct smb347_charger *smb, u8 reg, u8 val)
+{
+       int ret;
+
+       ret = i2c_smbus_write_byte_data(smb->client, reg, val);
+       if (ret < 0)
+               dev_warn(&smb->client->dev, "failed to write reg 0x%x: %d\n",
+                        reg, ret);
+       return ret;
+}
+
+/**
+ * smb347_update_status - updates the charging status
+ * @smb: pointer to smb347 charger instance
+ *
+ * Function checks status of the charging and updates internal state
+ * accordingly. Returns %0 if there is no change in status, %1 if the
+ * status has changed and negative errno in case of failure.
+ */
+static int smb347_update_status(struct smb347_charger *smb)
+{
+       bool usb = false;
+       bool dc = false;
+       int ret;
+
+       ret = smb347_read(smb, IRQSTAT_E);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Dc and usb are set depending on whether they are enabled in
+        * platform data _and_ whether corresponding undervoltage is set.
+        */
+       if (smb->pdata->use_mains)
+               dc = !(ret & IRQSTAT_E_DCIN_UV_STAT);
+       if (smb->pdata->use_usb)
+               usb = !(ret & IRQSTAT_E_USBIN_UV_STAT);
+
+       mutex_lock(&smb->lock);
+       ret = smb->mains_online != dc || smb->usb_online != usb;
+       smb->mains_online = dc;
+       smb->usb_online = usb;
+       mutex_unlock(&smb->lock);
+
+       return ret;
+}
+
+/*
+ * smb347_is_online - returns whether input power source is connected
+ * @smb: pointer to smb347 charger instance
+ *
+ * Returns %true if input power source is connected. Note that this is
+ * dependent on what platform has configured for usable power sources. For
+ * example if USB is disabled, this will return %false even if the USB
+ * cable is connected.
+ */
+static bool smb347_is_online(struct smb347_charger *smb)
+{
+       bool ret;
+
+       mutex_lock(&smb->lock);
+       ret = smb->usb_online || smb->mains_online;
+       mutex_unlock(&smb->lock);
+
+       return ret;
+}
+
+/**
+ * smb347_charging_status - returns status of charging
+ * @smb: pointer to smb347 charger instance
+ *
+ * Function returns charging status. %0 means no charging is in progress,
+ * %1 means pre-charging, %2 fast-charging and %3 taper-charging.
+ */
+static int smb347_charging_status(struct smb347_charger *smb)
+{
+       int ret;
+
+       if (!smb347_is_online(smb))
+               return 0;
+
+       ret = smb347_read(smb, STAT_C);
+       if (ret < 0)
+               return 0;
+
+       return (ret & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT;
+}
+
+static int smb347_charging_set(struct smb347_charger *smb, bool enable)
+{
+       int ret = 0;
+
+       if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) {
+               dev_dbg(&smb->client->dev,
+                       "charging enable/disable in SW disabled\n");
+               return 0;
+       }
+
+       mutex_lock(&smb->lock);
+       if (smb->charging_enabled != enable) {
+               ret = smb347_read(smb, CMD_A);
+               if (ret < 0)
+                       goto out;
+
+               smb->charging_enabled = enable;
+
+               if (enable)
+                       ret |= CMD_A_CHG_ENABLED;
+               else
+                       ret &= ~CMD_A_CHG_ENABLED;
+
+               ret = smb347_write(smb, CMD_A, ret);
+       }
+out:
+       mutex_unlock(&smb->lock);
+       return ret;
+}
+
+static inline int smb347_charging_enable(struct smb347_charger *smb)
+{
+       return smb347_charging_set(smb, true);
+}
+
+static inline int smb347_charging_disable(struct smb347_charger *smb)
+{
+       return smb347_charging_set(smb, false);
+}
+
+static int smb347_update_online(struct smb347_charger *smb)
+{
+       int ret;
+
+       /*
+        * Depending on whether valid power source is connected or not, we
+        * disable or enable the charging. We do it manually because it
+        * depends on how the platform has configured the valid inputs.
+        */
+       if (smb347_is_online(smb)) {
+               ret = smb347_charging_enable(smb);
+               if (ret < 0)
+                       dev_err(&smb->client->dev,
+                               "failed to enable charging\n");
+       } else {
+               ret = smb347_charging_disable(smb);
+               if (ret < 0)
+                       dev_err(&smb->client->dev,
+                               "failed to disable charging\n");
+       }
+
+       return ret;
+}
+
+static int smb347_set_charge_current(struct smb347_charger *smb)
+{
+       int ret, val;
+
+       ret = smb347_read(smb, CFG_CHARGE_CURRENT);
+       if (ret < 0)
+               return ret;
+
+       if (smb->pdata->max_charge_current) {
+               val = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl),
+                                   smb->pdata->max_charge_current);
+               if (val < 0)
+                       return val;
+
+               ret &= ~CFG_CHARGE_CURRENT_FCC_MASK;
+               ret |= val << CFG_CHARGE_CURRENT_FCC_SHIFT;
+       }
+
+       if (smb->pdata->pre_charge_current) {
+               val = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl),
+                                   smb->pdata->pre_charge_current);
+               if (val < 0)
+                       return val;
+
+               ret &= ~CFG_CHARGE_CURRENT_PCC_MASK;
+               ret |= val << CFG_CHARGE_CURRENT_PCC_SHIFT;
+       }
+
+       if (smb->pdata->termination_current) {
+               val = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl),
+                                   smb->pdata->termination_current);
+               if (val < 0)
+                       return val;
+
+               ret &= ~CFG_CHARGE_CURRENT_TC_MASK;
+               ret |= val;
+       }
+
+       return smb347_write(smb, CFG_CHARGE_CURRENT, ret);
+}
+
+static int smb347_set_current_limits(struct smb347_charger *smb)
+{
+       int ret, val;
+
+       ret = smb347_read(smb, CFG_CURRENT_LIMIT);
+       if (ret < 0)
+               return ret;
+
+       if (smb->pdata->mains_current_limit) {
+               val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+                                   smb->pdata->mains_current_limit);
+               if (val < 0)
+                       return val;
+
+               ret &= ~CFG_CURRENT_LIMIT_DC_MASK;
+               ret |= val << CFG_CURRENT_LIMIT_DC_SHIFT;
+       }
+
+       if (smb->pdata->usb_hc_current_limit) {
+               val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+                                   smb->pdata->usb_hc_current_limit);
+               if (val < 0)
+                       return val;
+
+               ret &= ~CFG_CURRENT_LIMIT_USB_MASK;
+               ret |= val;
+       }
+
+       return smb347_write(smb, CFG_CURRENT_LIMIT, ret);
+}
+
+static int smb347_set_voltage_limits(struct smb347_charger *smb)
+{
+       int ret, val;
+
+       ret = smb347_read(smb, CFG_FLOAT_VOLTAGE);
+       if (ret < 0)
+               return ret;
+
+       if (smb->pdata->pre_to_fast_voltage) {
+               val = smb->pdata->pre_to_fast_voltage;
+
+               /* uV */
+               val = clamp_val(val, 2400000, 3000000) - 2400000;
+               val /= 200000;
+
+               ret &= ~CFG_FLOAT_VOLTAGE_THRESHOLD_MASK;
+               ret |= val << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT;
+       }
+
+       if (smb->pdata->max_charge_voltage) {
+               val = smb->pdata->max_charge_voltage;
+
+               /* uV */
+               val = clamp_val(val, 3500000, 4500000) - 3500000;
+               val /= 20000;
+
+               ret |= val;
+       }
+
+       return smb347_write(smb, CFG_FLOAT_VOLTAGE, ret);
+}
+
+static int smb347_set_temp_limits(struct smb347_charger *smb)
+{
+       bool enable_therm_monitor = false;
+       int ret, val;
+
+       if (smb->pdata->chip_temp_threshold) {
+               val = smb->pdata->chip_temp_threshold;
+
+               /* degree C */
+               val = clamp_val(val, 100, 130) - 100;
+               val /= 10;
+
+               ret = smb347_read(smb, CFG_OTG);
+               if (ret < 0)
+                       return ret;
+
+               ret &= ~CFG_OTG_TEMP_THRESHOLD_MASK;
+               ret |= val << CFG_OTG_TEMP_THRESHOLD_SHIFT;
+
+               ret = smb347_write(smb, CFG_OTG, ret);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = smb347_read(smb, CFG_TEMP_LIMIT);
+       if (ret < 0)
+               return ret;
+
+       if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+               val = smb->pdata->soft_cold_temp_limit;
+
+               val = clamp_val(val, 0, 15);
+               val /= 5;
+               /* this goes from higher to lower so invert the value */
+               val = ~val & 0x3;
+
+               ret &= ~CFG_TEMP_LIMIT_SOFT_COLD_MASK;
+               ret |= val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT;
+
+               enable_therm_monitor = true;
+       }
+
+       if (smb->pdata->soft_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+               val = smb->pdata->soft_hot_temp_limit;
+
+               val = clamp_val(val, 40, 55) - 40;
+               val /= 5;
+
+               ret &= ~CFG_TEMP_LIMIT_SOFT_HOT_MASK;
+               ret |= val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT;
+
+               enable_therm_monitor = true;
+       }
+
+       if (smb->pdata->hard_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+               val = smb->pdata->hard_cold_temp_limit;
+
+               val = clamp_val(val, -5, 10) + 5;
+               val /= 5;
+               /* this goes from higher to lower so invert the value */
+               val = ~val & 0x3;
+
+               ret &= ~CFG_TEMP_LIMIT_HARD_COLD_MASK;
+               ret |= val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT;
+
+               enable_therm_monitor = true;
+       }
+
+       if (smb->pdata->hard_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+               val = smb->pdata->hard_hot_temp_limit;
+
+               val = clamp_val(val, 50, 65) - 50;
+               val /= 5;
+
+               ret &= ~CFG_TEMP_LIMIT_HARD_HOT_MASK;
+               ret |= val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT;
+
+               enable_therm_monitor = true;
+       }
+
+       ret = smb347_write(smb, CFG_TEMP_LIMIT, ret);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * If any of the temperature limits are set, we also enable the
+        * thermistor monitoring.
+        *
+        * When soft limits are hit, the device will start to compensate
+        * current and/or voltage depending on the configuration.
+        *
+        * When hard limit is hit, the device will suspend charging
+        * depending on the configuration.
+        */
+       if (enable_therm_monitor) {
+               ret = smb347_read(smb, CFG_THERM);
+               if (ret < 0)
+                       return ret;
+
+               ret &= ~CFG_THERM_MONITOR_DISABLED;
+
+               ret = smb347_write(smb, CFG_THERM, ret);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->suspend_on_hard_temp_limit) {
+               ret = smb347_read(smb, CFG_SYSOK);
+               if (ret < 0)
+                       return ret;
+
+               ret &= ~CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED;
+
+               ret = smb347_write(smb, CFG_SYSOK, ret);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->soft_temp_limit_compensation !=
+           SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) {
+               val = smb->pdata->soft_temp_limit_compensation & 0x3;
+
+               ret = smb347_read(smb, CFG_THERM);
+               if (ret < 0)
+                       return ret;
+
+               ret &= ~CFG_THERM_SOFT_HOT_COMPENSATION_MASK;
+               ret |= val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT;
+
+               ret &= ~CFG_THERM_SOFT_COLD_COMPENSATION_MASK;
+               ret |= val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT;
+
+               ret = smb347_write(smb, CFG_THERM, ret);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->charge_current_compensation) {
+               val = current_to_hw(ccc_tbl, ARRAY_SIZE(ccc_tbl),
+                                   smb->pdata->charge_current_compensation);
+               if (val < 0)
+                       return val;
+
+               ret = smb347_read(smb, CFG_OTG);
+               if (ret < 0)
+                       return ret;
+
+               ret &= ~CFG_OTG_CC_COMPENSATION_MASK;
+               ret |= (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT;
+
+               ret = smb347_write(smb, CFG_OTG, ret);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+/*
+ * smb347_set_writable - enables/disables writing to non-volatile registers
+ * @smb: pointer to smb347 charger instance
+ *
+ * You can enable/disable writing to the non-volatile configuration
+ * registers by calling this function.
+ *
+ * Returns %0 on success and negative errno in case of failure.
+ */
+static int smb347_set_writable(struct smb347_charger *smb, bool writable)
+{
+       int ret;
+
+       ret = smb347_read(smb, CMD_A);
+       if (ret < 0)
+               return ret;
+
+       if (writable)
+               ret |= CMD_A_ALLOW_WRITE;
+       else
+               ret &= ~CMD_A_ALLOW_WRITE;
+
+       return smb347_write(smb, CMD_A, ret);
+}
+
+static int smb347_hw_init(struct smb347_charger *smb)
+{
+       int ret;
+
+       ret = smb347_set_writable(smb, true);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Program the platform specific configuration values to the device
+        * first.
+        */
+       ret = smb347_set_charge_current(smb);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_set_current_limits(smb);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_set_voltage_limits(smb);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_set_temp_limits(smb);
+       if (ret < 0)
+               goto fail;
+
+       /* If USB charging is disabled we put the USB in suspend mode */
+       if (!smb->pdata->use_usb) {
+               ret = smb347_read(smb, CMD_A);
+               if (ret < 0)
+                       goto fail;
+
+               ret |= CMD_A_SUSPEND_ENABLED;
+
+               ret = smb347_write(smb, CMD_A, ret);
+               if (ret < 0)
+                       goto fail;
+       }
+
+       ret = smb347_read(smb, CFG_OTHER);
+       if (ret < 0)
+               goto fail;
+
+       /*
+        * If configured by platform data, we enable hardware Auto-OTG
+        * support for driving VBUS. Otherwise we disable it.
+        */
+       ret &= ~CFG_OTHER_RID_MASK;
+       if (smb->pdata->use_usb_otg)
+               ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG;
+
+       ret = smb347_write(smb, CFG_OTHER, ret);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_read(smb, CFG_PIN);
+       if (ret < 0)
+               goto fail;
+
+       /*
+        * Make the charging functionality controllable by a write to the
+        * command register unless pin control is specified in the platform
+        * data.
+        */
+       ret &= ~CFG_PIN_EN_CTRL_MASK;
+
+       switch (smb->pdata->enable_control) {
+       case SMB347_CHG_ENABLE_SW:
+               /* Do nothing, 0 means i2c control */
+               break;
+       case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW:
+               ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW;
+               break;
+       case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH:
+               ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH;
+               break;
+       }
+
+       /* Disable Automatic Power Source Detection (APSD) interrupt. */
+       ret &= ~CFG_PIN_EN_APSD_IRQ;
+
+       ret = smb347_write(smb, CFG_PIN, ret);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_update_status(smb);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_update_online(smb);
+
+fail:
+       smb347_set_writable(smb, false);
+       return ret;
+}
+
+static irqreturn_t smb347_interrupt(int irq, void *data)
+{
+       struct smb347_charger *smb = data;
+       int stat_c, irqstat_e, irqstat_c;
+       irqreturn_t ret = IRQ_NONE;
+
+       stat_c = smb347_read(smb, STAT_C);
+       if (stat_c < 0) {
+               dev_warn(&smb->client->dev, "reading STAT_C failed\n");
+               return IRQ_NONE;
+       }
+
+       irqstat_c = smb347_read(smb, IRQSTAT_C);
+       if (irqstat_c < 0) {
+               dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n");
+               return IRQ_NONE;
+       }
+
+       irqstat_e = smb347_read(smb, IRQSTAT_E);
+       if (irqstat_e < 0) {
+               dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n");
+               return IRQ_NONE;
+       }
+
+       /*
+        * If we get charger error we report the error back to user and
+        * disable charging.
+        */
+       if (stat_c & STAT_C_CHARGER_ERROR) {
+               dev_err(&smb->client->dev,
+                       "error in charger, disabling charging\n");
+
+               smb347_charging_disable(smb);
+               power_supply_changed(&smb->battery);
+
+               ret = IRQ_HANDLED;
+       }
+
+       /*
+        * If we reached the termination current the battery is charged and
+        * we can update the status now. Charging is automatically
+        * disabled by the hardware.
+        */
+       if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) {
+               if (irqstat_c & IRQSTAT_C_TERMINATION_STAT)
+                       power_supply_changed(&smb->battery);
+               ret = IRQ_HANDLED;
+       }
+
+       /*
+        * If we got an under voltage interrupt it means that AC/USB input
+        * was connected or disconnected.
+        */
+       if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) {
+               if (smb347_update_status(smb) > 0) {
+                       smb347_update_online(smb);
+                       power_supply_changed(&smb->mains);
+                       power_supply_changed(&smb->usb);
+               }
+               ret = IRQ_HANDLED;
+       }
+
+       return ret;
+}
+
+static int smb347_irq_set(struct smb347_charger *smb, bool enable)
+{
+       int ret;
+
+       ret = smb347_set_writable(smb, true);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Enable/disable interrupts for:
+        *      - under voltage
+        *      - termination current reached
+        *      - charger error
+        */
+       if (enable) {
+               ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV);
+               if (ret < 0)
+                       goto fail;
+
+               ret = smb347_write(smb, CFG_STATUS_IRQ,
+                                  CFG_STATUS_IRQ_TERMINATION_OR_TAPER);
+               if (ret < 0)
+                       goto fail;
+
+               ret = smb347_read(smb, CFG_PIN);
+               if (ret < 0)
+                       goto fail;
+
+               ret |= CFG_PIN_EN_CHARGER_ERROR;
+
+               ret = smb347_write(smb, CFG_PIN, ret);
+       } else {
+               ret = smb347_write(smb, CFG_FAULT_IRQ, 0);
+               if (ret < 0)
+                       goto fail;
+
+               ret = smb347_write(smb, CFG_STATUS_IRQ, 0);
+               if (ret < 0)
+                       goto fail;
+
+               ret = smb347_read(smb, CFG_PIN);
+               if (ret < 0)
+                       goto fail;
+
+               ret &= ~CFG_PIN_EN_CHARGER_ERROR;
+
+               ret = smb347_write(smb, CFG_PIN, ret);
+       }
+
+fail:
+       smb347_set_writable(smb, false);
+       return ret;
+}
+
+static inline int smb347_irq_enable(struct smb347_charger *smb)
+{
+       return smb347_irq_set(smb, true);
+}
+
+static inline int smb347_irq_disable(struct smb347_charger *smb)
+{
+       return smb347_irq_set(smb, false);
+}
+
+static int smb347_irq_init(struct smb347_charger *smb)
+{
+       const struct smb347_charger_platform_data *pdata = smb->pdata;
+       int ret, irq = gpio_to_irq(pdata->irq_gpio);
+
+       ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name);
+       if (ret < 0)
+               goto fail;
+
+       ret = request_threaded_irq(irq, NULL, smb347_interrupt,
+                                  IRQF_TRIGGER_FALLING, smb->client->name,
+                                  smb);
+       if (ret < 0)
+               goto fail_gpio;
+
+       ret = smb347_set_writable(smb, true);
+       if (ret < 0)
+               goto fail_irq;
+
+       /*
+        * Configure the STAT output to be suitable for interrupts: disable
+        * all other output (except interrupts) and make it active low.
+        */
+       ret = smb347_read(smb, CFG_STAT);
+       if (ret < 0)
+               goto fail_readonly;
+
+       ret &= ~CFG_STAT_ACTIVE_HIGH;
+       ret |= CFG_STAT_DISABLED;
+
+       ret = smb347_write(smb, CFG_STAT, ret);
+       if (ret < 0)
+               goto fail_readonly;
+
+       ret = smb347_irq_enable(smb);
+       if (ret < 0)
+               goto fail_readonly;
+
+       smb347_set_writable(smb, false);
+       smb->client->irq = irq;
+       return 0;
+
+fail_readonly:
+       smb347_set_writable(smb, false);
+fail_irq:
+       free_irq(irq, smb);
+fail_gpio:
+       gpio_free(pdata->irq_gpio);
+fail:
+       smb->client->irq = 0;
+       return ret;
+}
+
+static int smb347_mains_get_property(struct power_supply *psy,
+                                    enum power_supply_property prop,
+                                    union power_supply_propval *val)
+{
+       struct smb347_charger *smb =
+               container_of(psy, struct smb347_charger, mains);
+
+       if (prop == POWER_SUPPLY_PROP_ONLINE) {
+               val->intval = smb->mains_online;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static enum power_supply_property smb347_mains_properties[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int smb347_usb_get_property(struct power_supply *psy,
+                                  enum power_supply_property prop,
+                                  union power_supply_propval *val)
+{
+       struct smb347_charger *smb =
+               container_of(psy, struct smb347_charger, usb);
+
+       if (prop == POWER_SUPPLY_PROP_ONLINE) {
+               val->intval = smb->usb_online;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static enum power_supply_property smb347_usb_properties[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int smb347_battery_get_property(struct power_supply *psy,
+                                      enum power_supply_property prop,
+                                      union power_supply_propval *val)
+{
+       struct smb347_charger *smb =
+                       container_of(psy, struct smb347_charger, battery);
+       const struct smb347_charger_platform_data *pdata = smb->pdata;
+       int ret;
+
+       ret = smb347_update_status(smb);
+       if (ret < 0)
+               return ret;
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (!smb347_is_online(smb)) {
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+                       break;
+               }
+               if (smb347_charging_status(smb))
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               if (!smb347_is_online(smb))
+                       return -ENODATA;
+
+               /*
+                * We handle trickle and pre-charging the same, and taper
+                * and none the same.
+                */
+               switch (smb347_charging_status(smb)) {
+               case 1:
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+                       break;
+               case 2:
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+                       break;
+               default:
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+                       break;
+               }
+               break;
+
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = pdata->battery_info.technology;
+               break;
+
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = pdata->battery_info.voltage_min_design;
+               break;
+
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = pdata->battery_info.voltage_max_design;
+               break;
+
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (!smb347_is_online(smb))
+                       return -ENODATA;
+               ret = smb347_read(smb, STAT_A);
+               if (ret < 0)
+                       return ret;
+
+               ret &= STAT_A_FLOAT_VOLTAGE_MASK;
+               if (ret > 0x3d)
+                       ret = 0x3d;
+
+               val->intval = 3500000 + ret * 20000;
+               break;
+
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               if (!smb347_is_online(smb))
+                       return -ENODATA;
+
+               ret = smb347_read(smb, STAT_B);
+               if (ret < 0)
+                       return ret;
+
+               /*
+                * The current value is composition of FCC and PCC values
+                * and we can detect which table to use from bit 5.
+                */
+               if (ret & 0x20) {
+                       val->intval = hw_to_current(fcc_tbl,
+                                                   ARRAY_SIZE(fcc_tbl),
+                                                   ret & 7);
+               } else {
+                       ret >>= 3;
+                       val->intval = hw_to_current(pcc_tbl,
+                                                   ARRAY_SIZE(pcc_tbl),
+                                                   ret & 7);
+               }
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval = pdata->battery_info.charge_full_design;
+               break;
+
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = pdata->battery_info.name;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property smb347_battery_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+static int smb347_debugfs_show(struct seq_file *s, void *data)
+{
+       struct smb347_charger *smb = s->private;
+       int ret;
+       u8 reg;
+
+       seq_printf(s, "Control registers:\n");
+       seq_printf(s, "==================\n");
+       for (reg = CFG_CHARGE_CURRENT; reg <= CFG_ADDRESS; reg++) {
+               ret = smb347_read(smb, reg);
+               seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
+       }
+       seq_printf(s, "\n");
+
+       seq_printf(s, "Command registers:\n");
+       seq_printf(s, "==================\n");
+       ret = smb347_read(smb, CMD_A);
+       seq_printf(s, "0x%02x:\t0x%02x\n", CMD_A, ret);
+       ret = smb347_read(smb, CMD_B);
+       seq_printf(s, "0x%02x:\t0x%02x\n", CMD_B, ret);
+       ret = smb347_read(smb, CMD_C);
+       seq_printf(s, "0x%02x:\t0x%02x\n", CMD_C, ret);
+       seq_printf(s, "\n");
+
+       seq_printf(s, "Interrupt status registers:\n");
+       seq_printf(s, "===========================\n");
+       for (reg = IRQSTAT_A; reg <= IRQSTAT_F; reg++) {
+               ret = smb347_read(smb, reg);
+               seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
+       }
+       seq_printf(s, "\n");
+
+       seq_printf(s, "Status registers:\n");
+       seq_printf(s, "=================\n");
+       for (reg = STAT_A; reg <= STAT_E; reg++) {
+               ret = smb347_read(smb, reg);
+               seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
+       }
+
+       return 0;
+}
+
+static int smb347_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, smb347_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations smb347_debugfs_fops = {
+       .open           = smb347_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int smb347_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       static char *battery[] = { "smb347-battery" };
+       const struct smb347_charger_platform_data *pdata;
+       struct device *dev = &client->dev;
+       struct smb347_charger *smb;
+       int ret;
+
+       pdata = dev->platform_data;
+       if (!pdata)
+               return -EINVAL;
+
+       if (!pdata->use_mains && !pdata->use_usb)
+               return -EINVAL;
+
+       smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL);
+       if (!smb)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, smb);
+
+       mutex_init(&smb->lock);
+       smb->client = client;
+       smb->pdata = pdata;
+
+       ret = smb347_hw_init(smb);
+       if (ret < 0)
+               return ret;
+
+       smb->mains.name = "smb347-mains";
+       smb->mains.type = POWER_SUPPLY_TYPE_MAINS;
+       smb->mains.get_property = smb347_mains_get_property;
+       smb->mains.properties = smb347_mains_properties;
+       smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties);
+       smb->mains.supplied_to = battery;
+       smb->mains.num_supplicants = ARRAY_SIZE(battery);
+
+       smb->usb.name = "smb347-usb";
+       smb->usb.type = POWER_SUPPLY_TYPE_USB;
+       smb->usb.get_property = smb347_usb_get_property;
+       smb->usb.properties = smb347_usb_properties;
+       smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties);
+       smb->usb.supplied_to = battery;
+       smb->usb.num_supplicants = ARRAY_SIZE(battery);
+
+       smb->battery.name = "smb347-battery";
+       smb->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+       smb->battery.get_property = smb347_battery_get_property;
+       smb->battery.properties = smb347_battery_properties;
+       smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties);
+
+       ret = power_supply_register(dev, &smb->mains);
+       if (ret < 0)
+               return ret;
+
+       ret = power_supply_register(dev, &smb->usb);
+       if (ret < 0) {
+               power_supply_unregister(&smb->mains);
+               return ret;
+       }
+
+       ret = power_supply_register(dev, &smb->battery);
+       if (ret < 0) {
+               power_supply_unregister(&smb->usb);
+               power_supply_unregister(&smb->mains);
+               return ret;
+       }
+
+       /*
+        * Interrupt pin is optional. If it is connected, we setup the
+        * interrupt support here.
+        */
+       if (pdata->irq_gpio >= 0) {
+               ret = smb347_irq_init(smb);
+               if (ret < 0) {
+                       dev_warn(dev, "failed to initialize IRQ: %d\n", ret);
+                       dev_warn(dev, "disabling IRQ support\n");
+               }
+       }
+
+       smb->dentry = debugfs_create_file("smb347-regs", S_IRUSR, NULL, smb,
+                                         &smb347_debugfs_fops);
+       return 0;
+}
+
+static int smb347_remove(struct i2c_client *client)
+{
+       struct smb347_charger *smb = i2c_get_clientdata(client);
+
+       if (!IS_ERR_OR_NULL(smb->dentry))
+               debugfs_remove(smb->dentry);
+
+       if (client->irq) {
+               smb347_irq_disable(smb);
+               free_irq(client->irq, smb);
+               gpio_free(smb->pdata->irq_gpio);
+       }
+
+       power_supply_unregister(&smb->battery);
+       power_supply_unregister(&smb->usb);
+       power_supply_unregister(&smb->mains);
+       return 0;
+}
+
+static const struct i2c_device_id smb347_id[] = {
+       { "smb347", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, smb347_id);
+
+static struct i2c_driver smb347_driver = {
+       .driver = {
+               .name = "smb347",
+       },
+       .probe        = smb347_probe,
+       .remove       = __devexit_p(smb347_remove),
+       .id_table     = smb347_id,
+};
+
+static int __init smb347_init(void)
+{
+       return i2c_add_driver(&smb347_driver);
+}
+module_init(smb347_init);
+
+static void __exit smb347_exit(void)
+{
+       i2c_del_driver(&smb347_driver);
+}
+module_exit(smb347_exit);
+
+MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_DESCRIPTION("SMB347 battery charger driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i2c:smb347");
index 636ebb2a0e807bd44e28f902200cabdf16e399b9..8c9a607ea77a9ef3a02f7df2a0e94075d7448a78 100644 (file)
@@ -316,19 +316,7 @@ static struct i2c_driver z2_batt_driver = {
        .remove         = __devexit_p(z2_batt_remove),
        .id_table       = z2_batt_id,
 };
-
-static int __init z2_batt_init(void)
-{
-       return i2c_add_driver(&z2_batt_driver);
-}
-
-static void __exit z2_batt_exit(void)
-{
-       i2c_del_driver(&z2_batt_driver);
-}
-
-module_init(z2_batt_init);
-module_exit(z2_batt_exit);
+module_i2c_driver(z2_batt_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
index a06e608789e390661e860605d6de3a29db0b7243..29684c8142b0e62fd814c352d23d955e1aee567e 100644 (file)
@@ -619,6 +619,7 @@ config SCSI_ARCMSR
 
 source "drivers/scsi/megaraid/Kconfig.megaraid"
 source "drivers/scsi/mpt2sas/Kconfig"
+source "drivers/scsi/ufs/Kconfig"
 
 config SCSI_HPTIOP
        tristate "HighPoint RocketRAID 3xxx/4xxx Controller support"
index ad24e065b1e553c4efd620e6aa161362cf1e40dc..8deedeaf5608d0623b6fda4a28926bc763a9bd03 100644 (file)
@@ -108,6 +108,7 @@ obj-$(CONFIG_MEGARAID_LEGACY)       += megaraid.o
 obj-$(CONFIG_MEGARAID_NEWGEN)  += megaraid/
 obj-$(CONFIG_MEGARAID_SAS)     += megaraid/
 obj-$(CONFIG_SCSI_MPT2SAS)     += mpt2sas/
+obj-$(CONFIG_SCSI_UFSHCD)      += ufs/
 obj-$(CONFIG_SCSI_ACARD)       += atp870u.o
 obj-$(CONFIG_SCSI_SUNESP)      += esp_scsi.o   sun_esp.o
 obj-$(CONFIG_SCSI_GDTH)                += gdth.o
index 7d48700257a7aff109850ea641379779ebe17b0b..9328121804bbdf34b660cb6ecbb78db00fa41e77 100644 (file)
@@ -341,10 +341,10 @@ MODULE_PARM_DESC(aic79xx,
 "                              (0/256ms,1/128ms,2/64ms,3/32ms)\n"
 "      slowcrc                 Turn on the SLOWCRC bit (Rev B only)\n"          
 "\n"
-"      Sample /etc/modprobe.conf line:\n"
-"              Enable verbose logging\n"
-"              Set tag depth on Controller 2/Target 2 to 10 tags\n"
-"              Shorten the selection timeout to 128ms\n"
+"      Sample modprobe configuration file:\n"
+"      #       Enable verbose logging\n"
+"      #       Set tag depth on Controller 2/Target 2 to 10 tags\n"
+"      #       Shorten the selection timeout to 128ms\n"
 "\n"
 "      options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n"
 );
index c6251bb4f438fff7f15d36e0d65fae407667673d..5a477cdc780dcf1c3a8ecc0865ce9199a517daf8 100644 (file)
@@ -360,10 +360,10 @@ MODULE_PARM_DESC(aic7xxx,
 "      seltime:<int>           Selection Timeout\n"
 "                              (0/256ms,1/128ms,2/64ms,3/32ms)\n"
 "\n"
-"      Sample /etc/modprobe.conf line:\n"
-"              Toggle EISA/VLB probing\n"
-"              Set tag depth on Controller 1/Target 1 to 10 tags\n"
-"              Shorten the selection timeout to 128ms\n"
+"      Sample modprobe configuration file:\n"
+"      #       Toggle EISA/VLB probing\n"
+"      #       Set tag depth on Controller 1/Target 1 to 10 tags\n"
+"      #       Shorten the selection timeout to 128ms\n"
 "\n"
 "      options aic7xxx 'aic7xxx=probe_eisa_vl.tag_info:{{}.{.10}}.seltime:1'\n"
 );
index f29d5121d5ed2985af146a6416b0618293246cd8..68ce08552f699b6752cecfaa2c8a9174e8c7c634 100644 (file)
@@ -2582,7 +2582,7 @@ static int atp870u_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
         * this than via the PCI device table
         */
        if (ent->device == PCI_DEVICE_ID_ARTOP_AEC7610) {
-               error = pci_read_config_byte(pdev, PCI_CLASS_REVISION, &atpdev->chip_ver);
+               atpdev->chip_ver = pdev->revision;
                if (atpdev->chip_ver < 2)
                        goto err_eio;
        }
@@ -2601,7 +2601,7 @@ static int atp870u_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        base_io &= 0xfffffff8;
 
        if ((ent->device == ATP880_DEVID1)||(ent->device == ATP880_DEVID2)) {
-               error = pci_read_config_byte(pdev, PCI_CLASS_REVISION, &atpdev->chip_ver);
+               atpdev->chip_ver = pdev->revision;
                pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80);//JCC082803
 
                host_id = inb(base_io + 0x39);
index a796de9350541a4fcae375681bb911d1fc27ec00..4ad7e368bbc2505a0c0b86a637d6a718dd0a4dac 100644 (file)
@@ -225,9 +225,9 @@ struct bfa_faa_args_s {
 };
 
 struct bfa_iocfc_s {
+       bfa_fsm_t               fsm;
        struct bfa_s            *bfa;
        struct bfa_iocfc_cfg_s  cfg;
-       int                     action;
        u32             req_cq_pi[BFI_IOC_MAX_CQS];
        u32             rsp_cq_ci[BFI_IOC_MAX_CQS];
        u8              hw_qid[BFI_IOC_MAX_CQS];
@@ -236,7 +236,9 @@ struct bfa_iocfc_s {
        struct bfa_cb_qe_s      dis_hcb_qe;
        struct bfa_cb_qe_s      en_hcb_qe;
        struct bfa_cb_qe_s      stats_hcb_qe;
-       bfa_boolean_t           cfgdone;
+       bfa_boolean_t           submod_enabled;
+       bfa_boolean_t           cb_reqd;        /* Driver call back reqd */
+       bfa_status_t            op_status;      /* Status of bfa iocfc op */
 
        struct bfa_dma_s        cfg_info;
        struct bfi_iocfc_cfg_s *cfginfo;
@@ -341,8 +343,6 @@ void bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, u32 *nvecs,
 void bfa_hwct_msix_get_rme_range(struct bfa_s *bfa, u32 *start,
                                 u32 *end);
 void bfa_iocfc_get_bootwwns(struct bfa_s *bfa, u8 *nwwns, wwn_t *wwns);
-wwn_t bfa_iocfc_get_pwwn(struct bfa_s *bfa);
-wwn_t bfa_iocfc_get_nwwn(struct bfa_s *bfa);
 int bfa_iocfc_get_pbc_vports(struct bfa_s *bfa,
                                struct bfi_pbc_vport_s *pbc_vport);
 
@@ -428,7 +428,6 @@ bfa_status_t bfa_iocfc_israttr_set(struct bfa_s *bfa,
 
 void bfa_iocfc_enable(struct bfa_s *bfa);
 void bfa_iocfc_disable(struct bfa_s *bfa);
-void bfa_iocfc_cb_dconf_modinit(struct bfa_s *bfa, bfa_status_t status);
 #define bfa_timer_start(_bfa, _timer, _timercb, _arg, _timeout)                \
        bfa_timer_begin(&(_bfa)->timer_mod, _timer, _timercb, _arg, _timeout)
 
index 4bd546bcc240740fdadd04fd435828b4dc94f4f6..456e5762977df90bc7984d98bdae383797ad674a 100644 (file)
@@ -199,14 +199,432 @@ enum {
 #define DEF_CFG_NUM_SBOOT_TGTS         16
 #define DEF_CFG_NUM_SBOOT_LUNS         16
 
+/*
+ * IOCFC state machine definitions/declarations
+ */
+bfa_fsm_state_decl(bfa_iocfc, stopped, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, initing, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, dconf_read, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, init_cfg_wait,
+                  struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, init_cfg_done,
+                  struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, operational,
+                  struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, dconf_write,
+                  struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, stopping, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, enabling, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, cfg_wait, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, disabling, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, disabled, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, failed, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, init_failed,
+                  struct bfa_iocfc_s, enum iocfc_event);
+
 /*
  * forward declaration for IOC FC functions
  */
+static void bfa_iocfc_start_submod(struct bfa_s *bfa);
+static void bfa_iocfc_disable_submod(struct bfa_s *bfa);
+static void bfa_iocfc_send_cfg(void *bfa_arg);
 static void bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status);
 static void bfa_iocfc_disable_cbfn(void *bfa_arg);
 static void bfa_iocfc_hbfail_cbfn(void *bfa_arg);
 static void bfa_iocfc_reset_cbfn(void *bfa_arg);
 static struct bfa_ioc_cbfn_s bfa_iocfc_cbfn;
+static void bfa_iocfc_init_cb(void *bfa_arg, bfa_boolean_t complete);
+static void bfa_iocfc_stop_cb(void *bfa_arg, bfa_boolean_t compl);
+static void bfa_iocfc_enable_cb(void *bfa_arg, bfa_boolean_t compl);
+static void bfa_iocfc_disable_cb(void *bfa_arg, bfa_boolean_t compl);
+
+static void
+bfa_iocfc_sm_stopped_entry(struct bfa_iocfc_s *iocfc)
+{
+}
+
+static void
+bfa_iocfc_sm_stopped(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_INIT:
+       case IOCFC_E_ENABLE:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_initing);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_initing_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_ioc_enable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_initing(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_IOC_ENABLED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_read);
+               break;
+       case IOCFC_E_IOC_FAILED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_dconf_read_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_dconf_modinit(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_dconf_read(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_DCONF_DONE:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_cfg_wait);
+               break;
+       case IOCFC_E_IOC_FAILED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_init_cfg_wait_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_iocfc_send_cfg(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_init_cfg_wait(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_CFG_DONE:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_cfg_done);
+               break;
+       case IOCFC_E_IOC_FAILED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_init_cfg_done_entry(struct bfa_iocfc_s *iocfc)
+{
+       iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+       bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.init_hcb_qe,
+                    bfa_iocfc_init_cb, iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_init_cfg_done(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_START:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_operational);
+               break;
+       case IOCFC_E_STOP:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+               break;
+       case IOCFC_E_DISABLE:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+               break;
+       case IOCFC_E_IOC_FAILED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_operational_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_fcport_init(iocfc->bfa);
+       bfa_iocfc_start_submod(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_operational(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_STOP:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+               break;
+       case IOCFC_E_DISABLE:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+               break;
+       case IOCFC_E_IOC_FAILED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_dconf_write_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_dconf_modexit(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_dconf_write(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_DCONF_DONE:
+       case IOCFC_E_IOC_FAILED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_stopping_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_ioc_disable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_stopping(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_IOC_DISABLED:
+               bfa_isr_disable(iocfc->bfa);
+               bfa_iocfc_disable_submod(iocfc->bfa);
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopped);
+               iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+               bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.stop_hcb_qe,
+                            bfa_iocfc_stop_cb, iocfc->bfa);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_enabling_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_ioc_enable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_enabling(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_IOC_ENABLED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_cfg_wait);
+               break;
+       case IOCFC_E_IOC_FAILED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+
+               if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE)
+                       break;
+
+               iocfc->bfa->iocfc.op_status = BFA_STATUS_FAILED;
+               bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.en_hcb_qe,
+                            bfa_iocfc_enable_cb, iocfc->bfa);
+               iocfc->bfa->iocfc.cb_reqd = BFA_FALSE;
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_cfg_wait_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_iocfc_send_cfg(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_cfg_wait(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_CFG_DONE:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_operational);
+               if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE)
+                       break;
+
+               iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+               bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.en_hcb_qe,
+                            bfa_iocfc_enable_cb, iocfc->bfa);
+               iocfc->bfa->iocfc.cb_reqd = BFA_FALSE;
+               break;
+       case IOCFC_E_IOC_FAILED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+               if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE)
+                       break;
+
+               iocfc->bfa->iocfc.op_status = BFA_STATUS_FAILED;
+               bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.en_hcb_qe,
+                            bfa_iocfc_enable_cb, iocfc->bfa);
+               iocfc->bfa->iocfc.cb_reqd = BFA_FALSE;
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_disabling_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_ioc_disable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_disabling(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_IOC_DISABLED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabled);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_disabled_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_isr_disable(iocfc->bfa);
+       bfa_iocfc_disable_submod(iocfc->bfa);
+       iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+       bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.dis_hcb_qe,
+                    bfa_iocfc_disable_cb, iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_disabled(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_STOP:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+               break;
+       case IOCFC_E_ENABLE:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_enabling);
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_failed_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_isr_disable(iocfc->bfa);
+       bfa_iocfc_disable_submod(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_failed(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_STOP:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+               break;
+       case IOCFC_E_DISABLE:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+               break;
+       case IOCFC_E_IOC_ENABLED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_cfg_wait);
+               break;
+       case IOCFC_E_IOC_FAILED:
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
+
+static void
+bfa_iocfc_sm_init_failed_entry(struct bfa_iocfc_s *iocfc)
+{
+       bfa_isr_disable(iocfc->bfa);
+       iocfc->bfa->iocfc.op_status = BFA_STATUS_FAILED;
+       bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.init_hcb_qe,
+                    bfa_iocfc_init_cb, iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_init_failed(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+       bfa_trc(iocfc->bfa, event);
+
+       switch (event) {
+       case IOCFC_E_STOP:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+               break;
+       case IOCFC_E_DISABLE:
+               bfa_ioc_disable(&iocfc->bfa->ioc);
+               break;
+       case IOCFC_E_IOC_ENABLED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_read);
+               break;
+       case IOCFC_E_IOC_DISABLED:
+               bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopped);
+               iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+               bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.dis_hcb_qe,
+                            bfa_iocfc_disable_cb, iocfc->bfa);
+               break;
+       case IOCFC_E_IOC_FAILED:
+               break;
+       default:
+               bfa_sm_fault(iocfc->bfa, event);
+               break;
+       }
+}
 
 /*
  * BFA Interrupt handling functions
@@ -231,16 +649,19 @@ bfa_reqq_resume(struct bfa_s *bfa, int qid)
        }
 }
 
-static inline void
+bfa_boolean_t
 bfa_isr_rspq(struct bfa_s *bfa, int qid)
 {
        struct bfi_msg_s *m;
        u32     pi, ci;
        struct list_head *waitq;
+       bfa_boolean_t ret;
 
        ci = bfa_rspq_ci(bfa, qid);
        pi = bfa_rspq_pi(bfa, qid);
 
+       ret = (ci != pi);
+
        while (ci != pi) {
                m = bfa_rspq_elem(bfa, qid, ci);
                WARN_ON(m->mhdr.msg_class >= BFI_MC_MAX);
@@ -260,6 +681,8 @@ bfa_isr_rspq(struct bfa_s *bfa, int qid)
        waitq = bfa_reqq(bfa, qid);
        if (!list_empty(waitq))
                bfa_reqq_resume(bfa, qid);
+
+       return ret;
 }
 
 static inline void
@@ -320,6 +743,7 @@ bfa_intx(struct bfa_s *bfa)
 {
        u32 intr, qintr;
        int queue;
+       bfa_boolean_t rspq_comp = BFA_FALSE;
 
        intr = readl(bfa->iocfc.bfa_regs.intr_status);
 
@@ -332,11 +756,12 @@ bfa_intx(struct bfa_s *bfa)
         */
        if (bfa->queue_process) {
                for (queue = 0; queue < BFI_IOC_MAX_CQS; queue++)
-                       bfa_isr_rspq(bfa, queue);
+                       if (bfa_isr_rspq(bfa, queue))
+                               rspq_comp = BFA_TRUE;
        }
 
        if (!intr)
-               return BFA_TRUE;
+               return (qintr | rspq_comp) ? BFA_TRUE : BFA_FALSE;
 
        /*
         * CPE completion queue interrupt
@@ -525,11 +950,9 @@ bfa_iocfc_send_cfg(void *bfa_arg)
         * Enable interrupt coalescing if it is driver init path
         * and not ioc disable/enable path.
         */
-       if (!iocfc->cfgdone)
+       if (bfa_fsm_cmp_state(iocfc, bfa_iocfc_sm_init_cfg_wait))
                cfg_info->intr_attr.coalesce = BFA_TRUE;
 
-       iocfc->cfgdone = BFA_FALSE;
-
        /*
         * dma map IOC configuration itself
         */
@@ -549,8 +972,6 @@ bfa_iocfc_init_mem(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
 
        bfa->bfad = bfad;
        iocfc->bfa = bfa;
-       iocfc->action = BFA_IOCFC_ACT_NONE;
-
        iocfc->cfg = *cfg;
 
        /*
@@ -683,6 +1104,8 @@ bfa_iocfc_start_submod(struct bfa_s *bfa)
 
        for (i = 0; hal_mods[i]; i++)
                hal_mods[i]->start(bfa);
+
+       bfa->iocfc.submod_enabled = BFA_TRUE;
 }
 
 /*
@@ -693,8 +1116,13 @@ bfa_iocfc_disable_submod(struct bfa_s *bfa)
 {
        int             i;
 
+       if (bfa->iocfc.submod_enabled == BFA_FALSE)
+               return;
+
        for (i = 0; hal_mods[i]; i++)
                hal_mods[i]->iocdisable(bfa);
+
+       bfa->iocfc.submod_enabled = BFA_FALSE;
 }
 
 static void
@@ -702,15 +1130,8 @@ bfa_iocfc_init_cb(void *bfa_arg, bfa_boolean_t complete)
 {
        struct bfa_s    *bfa = bfa_arg;
 
-       if (complete) {
-               if (bfa->iocfc.cfgdone && BFA_DCONF_MOD(bfa)->flashdone)
-                       bfa_cb_init(bfa->bfad, BFA_STATUS_OK);
-               else
-                       bfa_cb_init(bfa->bfad, BFA_STATUS_FAILED);
-       } else {
-               if (bfa->iocfc.cfgdone)
-                       bfa->iocfc.action = BFA_IOCFC_ACT_NONE;
-       }
+       if (complete)
+               bfa_cb_init(bfa->bfad, bfa->iocfc.op_status);
 }
 
 static void
@@ -721,8 +1142,6 @@ bfa_iocfc_stop_cb(void *bfa_arg, bfa_boolean_t compl)
 
        if (compl)
                complete(&bfad->comp);
-       else
-               bfa->iocfc.action = BFA_IOCFC_ACT_NONE;
 }
 
 static void
@@ -794,8 +1213,6 @@ bfa_iocfc_cfgrsp(struct bfa_s *bfa)
        fwcfg->num_uf_bufs    = be16_to_cpu(fwcfg->num_uf_bufs);
        fwcfg->num_rports     = be16_to_cpu(fwcfg->num_rports);
 
-       iocfc->cfgdone = BFA_TRUE;
-
        /*
         * configure queue register offsets as learnt from firmware
         */
@@ -811,22 +1228,13 @@ bfa_iocfc_cfgrsp(struct bfa_s *bfa)
         */
        bfa_msix_queue_install(bfa);
 
-       /*
-        * Configuration is complete - initialize/start submodules
-        */
-       bfa_fcport_init(bfa);
-
-       if (iocfc->action == BFA_IOCFC_ACT_INIT) {
-               if (BFA_DCONF_MOD(bfa)->flashdone == BFA_TRUE)
-                       bfa_cb_queue(bfa, &iocfc->init_hcb_qe,
-                               bfa_iocfc_init_cb, bfa);
-       } else {
-               if (bfa->iocfc.action == BFA_IOCFC_ACT_ENABLE)
-                       bfa_cb_queue(bfa, &bfa->iocfc.en_hcb_qe,
-                                       bfa_iocfc_enable_cb, bfa);
-               bfa_iocfc_start_submod(bfa);
+       if (bfa->iocfc.cfgrsp->pbc_cfg.pbc_pwwn != 0) {
+               bfa->ioc.attr->pwwn = bfa->iocfc.cfgrsp->pbc_cfg.pbc_pwwn;
+               bfa->ioc.attr->nwwn = bfa->iocfc.cfgrsp->pbc_cfg.pbc_nwwn;
+               bfa_fsm_send_event(iocfc, IOCFC_E_CFG_DONE);
        }
 }
+
 void
 bfa_iocfc_reset_queues(struct bfa_s *bfa)
 {
@@ -840,6 +1248,23 @@ bfa_iocfc_reset_queues(struct bfa_s *bfa)
        }
 }
 
+/*
+ *     Process FAA pwwn msg from fw.
+ */
+static void
+bfa_iocfc_process_faa_addr(struct bfa_s *bfa, struct bfi_faa_addr_msg_s *msg)
+{
+       struct bfa_iocfc_s              *iocfc   = &bfa->iocfc;
+       struct bfi_iocfc_cfgrsp_s       *cfgrsp  = iocfc->cfgrsp;
+
+       cfgrsp->pbc_cfg.pbc_pwwn = msg->pwwn;
+       cfgrsp->pbc_cfg.pbc_nwwn = msg->nwwn;
+
+       bfa->ioc.attr->pwwn = msg->pwwn;
+       bfa->ioc.attr->nwwn = msg->nwwn;
+       bfa_fsm_send_event(iocfc, IOCFC_E_CFG_DONE);
+}
+
 /* Fabric Assigned Address specific functions */
 
 /*
@@ -855,83 +1280,12 @@ bfa_faa_validate_request(struct bfa_s *bfa)
                if ((ioc_type != BFA_IOC_TYPE_FC) || bfa_mfg_is_mezz(card_type))
                        return BFA_STATUS_FEATURE_NOT_SUPPORTED;
        } else {
-               if (!bfa_ioc_is_acq_addr(&bfa->ioc))
-                       return BFA_STATUS_IOC_NON_OP;
+               return BFA_STATUS_IOC_NON_OP;
        }
 
        return BFA_STATUS_OK;
 }
 
-bfa_status_t
-bfa_faa_enable(struct bfa_s *bfa, bfa_cb_iocfc_t cbfn, void *cbarg)
-{
-       struct bfi_faa_en_dis_s faa_enable_req;
-       struct bfa_iocfc_s      *iocfc = &bfa->iocfc;
-       bfa_status_t            status;
-
-       iocfc->faa_args.faa_cb.faa_cbfn = cbfn;
-       iocfc->faa_args.faa_cb.faa_cbarg = cbarg;
-
-       status = bfa_faa_validate_request(bfa);
-       if (status != BFA_STATUS_OK)
-               return status;
-
-       if (iocfc->faa_args.busy == BFA_TRUE)
-               return BFA_STATUS_DEVBUSY;
-
-       if (iocfc->faa_args.faa_state == BFA_FAA_ENABLED)
-               return BFA_STATUS_FAA_ENABLED;
-
-       if (bfa_fcport_is_trunk_enabled(bfa))
-               return BFA_STATUS_ERROR_TRUNK_ENABLED;
-
-       bfa_fcport_cfg_faa(bfa, BFA_FAA_ENABLED);
-       iocfc->faa_args.busy = BFA_TRUE;
-
-       memset(&faa_enable_req, 0, sizeof(struct bfi_faa_en_dis_s));
-       bfi_h2i_set(faa_enable_req.mh, BFI_MC_IOCFC,
-               BFI_IOCFC_H2I_FAA_ENABLE_REQ, bfa_fn_lpu(bfa));
-
-       bfa_ioc_mbox_send(&bfa->ioc, &faa_enable_req,
-                       sizeof(struct bfi_faa_en_dis_s));
-
-       return BFA_STATUS_OK;
-}
-
-bfa_status_t
-bfa_faa_disable(struct bfa_s *bfa, bfa_cb_iocfc_t cbfn,
-               void *cbarg)
-{
-       struct bfi_faa_en_dis_s faa_disable_req;
-       struct bfa_iocfc_s      *iocfc = &bfa->iocfc;
-       bfa_status_t            status;
-
-       iocfc->faa_args.faa_cb.faa_cbfn = cbfn;
-       iocfc->faa_args.faa_cb.faa_cbarg = cbarg;
-
-       status = bfa_faa_validate_request(bfa);
-       if (status != BFA_STATUS_OK)
-               return status;
-
-       if (iocfc->faa_args.busy == BFA_TRUE)
-               return BFA_STATUS_DEVBUSY;
-
-       if (iocfc->faa_args.faa_state == BFA_FAA_DISABLED)
-               return BFA_STATUS_FAA_DISABLED;
-
-       bfa_fcport_cfg_faa(bfa, BFA_FAA_DISABLED);
-       iocfc->faa_args.busy = BFA_TRUE;
-
-       memset(&faa_disable_req, 0, sizeof(struct bfi_faa_en_dis_s));
-       bfi_h2i_set(faa_disable_req.mh, BFI_MC_IOCFC,
-               BFI_IOCFC_H2I_FAA_DISABLE_REQ, bfa_fn_lpu(bfa));
-
-       bfa_ioc_mbox_send(&bfa->ioc, &faa_disable_req,
-               sizeof(struct bfi_faa_en_dis_s));
-
-       return BFA_STATUS_OK;
-}
-
 bfa_status_t
 bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
                bfa_cb_iocfc_t cbfn, void *cbarg)
@@ -962,38 +1316,6 @@ bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
        return BFA_STATUS_OK;
 }
 
-/*
- *     FAA enable response
- */
-static void
-bfa_faa_enable_reply(struct bfa_iocfc_s *iocfc,
-               struct bfi_faa_en_dis_rsp_s *rsp)
-{
-       void    *cbarg = iocfc->faa_args.faa_cb.faa_cbarg;
-       bfa_status_t    status = rsp->status;
-
-       WARN_ON(!iocfc->faa_args.faa_cb.faa_cbfn);
-
-       iocfc->faa_args.faa_cb.faa_cbfn(cbarg, status);
-       iocfc->faa_args.busy = BFA_FALSE;
-}
-
-/*
- *     FAA disable response
- */
-static void
-bfa_faa_disable_reply(struct bfa_iocfc_s *iocfc,
-               struct bfi_faa_en_dis_rsp_s *rsp)
-{
-       void    *cbarg = iocfc->faa_args.faa_cb.faa_cbarg;
-       bfa_status_t    status = rsp->status;
-
-       WARN_ON(!iocfc->faa_args.faa_cb.faa_cbfn);
-
-       iocfc->faa_args.faa_cb.faa_cbfn(cbarg, status);
-       iocfc->faa_args.busy = BFA_FALSE;
-}
-
 /*
  *     FAA query response
  */
@@ -1023,25 +1345,10 @@ bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status)
 {
        struct bfa_s    *bfa = bfa_arg;
 
-       if (status == BFA_STATUS_FAA_ACQ_ADDR) {
-               bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe,
-                               bfa_iocfc_init_cb, bfa);
-               return;
-       }
-
-       if (status != BFA_STATUS_OK) {
-               bfa_isr_disable(bfa);
-               if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT)
-                       bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe,
-                                    bfa_iocfc_init_cb, bfa);
-               else if (bfa->iocfc.action == BFA_IOCFC_ACT_ENABLE)
-                       bfa_cb_queue(bfa, &bfa->iocfc.en_hcb_qe,
-                                       bfa_iocfc_enable_cb, bfa);
-               return;
-       }
-
-       bfa_iocfc_send_cfg(bfa);
-       bfa_dconf_modinit(bfa);
+       if (status == BFA_STATUS_OK)
+               bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_ENABLED);
+       else
+               bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_FAILED);
 }
 
 /*
@@ -1052,17 +1359,7 @@ bfa_iocfc_disable_cbfn(void *bfa_arg)
 {
        struct bfa_s    *bfa = bfa_arg;
 
-       bfa_isr_disable(bfa);
-       bfa_iocfc_disable_submod(bfa);
-
-       if (bfa->iocfc.action == BFA_IOCFC_ACT_STOP)
-               bfa_cb_queue(bfa, &bfa->iocfc.stop_hcb_qe, bfa_iocfc_stop_cb,
-                            bfa);
-       else {
-               WARN_ON(bfa->iocfc.action != BFA_IOCFC_ACT_DISABLE);
-               bfa_cb_queue(bfa, &bfa->iocfc.dis_hcb_qe, bfa_iocfc_disable_cb,
-                            bfa);
-       }
+       bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_DISABLED);
 }
 
 /*
@@ -1074,13 +1371,7 @@ bfa_iocfc_hbfail_cbfn(void *bfa_arg)
        struct bfa_s    *bfa = bfa_arg;
 
        bfa->queue_process = BFA_FALSE;
-
-       bfa_isr_disable(bfa);
-       bfa_iocfc_disable_submod(bfa);
-
-       if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT)
-               bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe, bfa_iocfc_init_cb,
-                            bfa);
+       bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_FAILED);
 }
 
 /*
@@ -1095,7 +1386,6 @@ bfa_iocfc_reset_cbfn(void *bfa_arg)
        bfa_isr_enable(bfa);
 }
 
-
 /*
  * Query IOC memory requirement information.
  */
@@ -1171,6 +1461,12 @@ bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
        INIT_LIST_HEAD(&bfa->comp_q);
        for (i = 0; i < BFI_IOC_MAX_CQS; i++)
                INIT_LIST_HEAD(&bfa->reqq_waitq[i]);
+
+       bfa->iocfc.cb_reqd = BFA_FALSE;
+       bfa->iocfc.op_status = BFA_STATUS_OK;
+       bfa->iocfc.submod_enabled = BFA_FALSE;
+
+       bfa_fsm_set_state(&bfa->iocfc, bfa_iocfc_sm_stopped);
 }
 
 /*
@@ -1179,8 +1475,7 @@ bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
 void
 bfa_iocfc_init(struct bfa_s *bfa)
 {
-       bfa->iocfc.action = BFA_IOCFC_ACT_INIT;
-       bfa_ioc_enable(&bfa->ioc);
+       bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_INIT);
 }
 
 /*
@@ -1190,8 +1485,7 @@ bfa_iocfc_init(struct bfa_s *bfa)
 void
 bfa_iocfc_start(struct bfa_s *bfa)
 {
-       if (bfa->iocfc.cfgdone)
-               bfa_iocfc_start_submod(bfa);
+       bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_START);
 }
 
 /*
@@ -1201,12 +1495,8 @@ bfa_iocfc_start(struct bfa_s *bfa)
 void
 bfa_iocfc_stop(struct bfa_s *bfa)
 {
-       bfa->iocfc.action = BFA_IOCFC_ACT_STOP;
-
        bfa->queue_process = BFA_FALSE;
-       bfa_dconf_modexit(bfa);
-       if (BFA_DCONF_MOD(bfa)->flashdone == BFA_TRUE)
-               bfa_ioc_disable(&bfa->ioc);
+       bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_STOP);
 }
 
 void
@@ -1226,13 +1516,9 @@ bfa_iocfc_isr(void *bfaarg, struct bfi_mbmsg_s *m)
        case BFI_IOCFC_I2H_UPDATEQ_RSP:
                iocfc->updateq_cbfn(iocfc->updateq_cbarg, BFA_STATUS_OK);
                break;
-       case BFI_IOCFC_I2H_FAA_ENABLE_RSP:
-               bfa_faa_enable_reply(iocfc,
-                       (struct bfi_faa_en_dis_rsp_s *)msg);
-               break;
-       case BFI_IOCFC_I2H_FAA_DISABLE_RSP:
-               bfa_faa_disable_reply(iocfc,
-                       (struct bfi_faa_en_dis_rsp_s *)msg);
+       case BFI_IOCFC_I2H_ADDR_MSG:
+               bfa_iocfc_process_faa_addr(bfa,
+                               (struct bfi_faa_addr_msg_s *)msg);
                break;
        case BFI_IOCFC_I2H_FAA_QUERY_RSP:
                bfa_faa_query_reply(iocfc, (bfi_faa_query_rsp_t *)msg);
@@ -1306,8 +1592,8 @@ bfa_iocfc_enable(struct bfa_s *bfa)
 {
        bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0,
                     "IOC Enable");
-       bfa->iocfc.action = BFA_IOCFC_ACT_ENABLE;
-       bfa_ioc_enable(&bfa->ioc);
+       bfa->iocfc.cb_reqd = BFA_TRUE;
+       bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_ENABLE);
 }
 
 void
@@ -1315,17 +1601,16 @@ bfa_iocfc_disable(struct bfa_s *bfa)
 {
        bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0,
                     "IOC Disable");
-       bfa->iocfc.action = BFA_IOCFC_ACT_DISABLE;
 
        bfa->queue_process = BFA_FALSE;
-       bfa_ioc_disable(&bfa->ioc);
+       bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_DISABLE);
 }
 
-
 bfa_boolean_t
 bfa_iocfc_is_operational(struct bfa_s *bfa)
 {
-       return bfa_ioc_is_operational(&bfa->ioc) && bfa->iocfc.cfgdone;
+       return bfa_ioc_is_operational(&bfa->ioc) &&
+               bfa_fsm_cmp_state(&bfa->iocfc, bfa_iocfc_sm_operational);
 }
 
 /*
@@ -1567,16 +1852,6 @@ bfa_comp_free(struct bfa_s *bfa, struct list_head *comp_q)
        }
 }
 
-void
-bfa_iocfc_cb_dconf_modinit(struct bfa_s *bfa, bfa_status_t status)
-{
-       if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT) {
-               if (bfa->iocfc.cfgdone == BFA_TRUE)
-                       bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe,
-                               bfa_iocfc_init_cb, bfa);
-       }
-}
-
 /*
  * Return the list of PCI vendor/device id lists supported by this
  * BFA instance.
index cb07c628b2f1856a3a26dadba052e2587edd3ae7..36756ce0e58f63dcb34736ef5834c7641bdcb323 100644 (file)
@@ -52,7 +52,7 @@ struct bfa_iocfc_fwcfg_s {
        u16             num_uf_bufs;    /*  unsolicited recv buffers    */
        u8              num_cqs;
        u8              fw_tick_res;    /*  FW clock resolution in ms */
-       u8              rsvd[2];
+       u8              rsvd[6];
 };
 #pragma pack()
 
index d4f951fe753eecb1fbccade0e96e5adde904b986..5d2a1307e5cea333a73356a56ff4a054af5009d2 100644 (file)
@@ -5717,6 +5717,8 @@ bfa_fcs_vport_free(struct bfa_fcs_vport_s *vport)
 
        if (vport_drv->comp_del)
                complete(vport_drv->comp_del);
+       else
+               kfree(vport_drv);
 
        bfa_lps_delete(vport->lps);
 }
index 52628d5d3c9b0663486833db47eda3d449a60aa9..fe0463a1db0456993ac9c9de8de991d6e6ce9941 100644 (file)
@@ -2169,7 +2169,10 @@ bfa_fcs_rport_update(struct bfa_fcs_rport_s *rport, struct fc_logi_s *plogi)
         * - MAX receive frame size
         */
        rport->cisc = plogi->csp.cisc;
-       rport->maxfrsize = be16_to_cpu(plogi->class3.rxsz);
+       if (be16_to_cpu(plogi->class3.rxsz) < be16_to_cpu(plogi->csp.rxsz))
+               rport->maxfrsize = be16_to_cpu(plogi->class3.rxsz);
+       else
+               rport->maxfrsize = be16_to_cpu(plogi->csp.rxsz);
 
        bfa_trc(port->fcs, be16_to_cpu(plogi->csp.bbcred));
        bfa_trc(port->fcs, port->fabric->bb_credit);
index eca7ab78085bef2664d63d173e4a5510cdb61cfd..14e6284e48e4110b4c907b06dacb311c06c26f77 100644 (file)
@@ -88,7 +88,6 @@ static void bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc);
 static void bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc);
 static void bfa_ioc_mbox_flush(struct bfa_ioc_s *ioc);
 static void bfa_ioc_recover(struct bfa_ioc_s *ioc);
-static void bfa_ioc_check_attr_wwns(struct bfa_ioc_s *ioc);
 static void bfa_ioc_event_notify(struct bfa_ioc_s *ioc ,
                                enum bfa_ioc_event_e event);
 static void bfa_ioc_disable_comp(struct bfa_ioc_s *ioc);
@@ -97,7 +96,6 @@ static void bfa_ioc_debug_save_ftrc(struct bfa_ioc_s *ioc);
 static void bfa_ioc_fail_notify(struct bfa_ioc_s *ioc);
 static void bfa_ioc_pf_fwmismatch(struct bfa_ioc_s *ioc);
 
-
 /*
  * IOC state machine definitions/declarations
  */
@@ -114,7 +112,6 @@ enum ioc_event {
        IOC_E_HWERROR           = 10,   /*  hardware error interrupt    */
        IOC_E_TIMEOUT           = 11,   /*  timeout                     */
        IOC_E_HWFAILED          = 12,   /*  PCI mapping failure notice  */
-       IOC_E_FWRSP_ACQ_ADDR    = 13,   /*  Acquiring address           */
 };
 
 bfa_fsm_state_decl(bfa_ioc, uninit, struct bfa_ioc_s, enum ioc_event);
@@ -127,7 +124,6 @@ bfa_fsm_state_decl(bfa_ioc, fail, struct bfa_ioc_s, enum ioc_event);
 bfa_fsm_state_decl(bfa_ioc, disabling, struct bfa_ioc_s, enum ioc_event);
 bfa_fsm_state_decl(bfa_ioc, disabled, struct bfa_ioc_s, enum ioc_event);
 bfa_fsm_state_decl(bfa_ioc, hwfail, struct bfa_ioc_s, enum ioc_event);
-bfa_fsm_state_decl(bfa_ioc, acq_addr, struct bfa_ioc_s, enum ioc_event);
 
 static struct bfa_sm_table_s ioc_sm_table[] = {
        {BFA_SM(bfa_ioc_sm_uninit), BFA_IOC_UNINIT},
@@ -140,7 +136,6 @@ static struct bfa_sm_table_s ioc_sm_table[] = {
        {BFA_SM(bfa_ioc_sm_disabling), BFA_IOC_DISABLING},
        {BFA_SM(bfa_ioc_sm_disabled), BFA_IOC_DISABLED},
        {BFA_SM(bfa_ioc_sm_hwfail), BFA_IOC_HWFAIL},
-       {BFA_SM(bfa_ioc_sm_acq_addr), BFA_IOC_ACQ_ADDR},
 };
 
 /*
@@ -371,17 +366,9 @@ bfa_ioc_sm_getattr(struct bfa_ioc_s *ioc, enum ioc_event event)
        switch (event) {
        case IOC_E_FWRSP_GETATTR:
                bfa_ioc_timer_stop(ioc);
-               bfa_ioc_check_attr_wwns(ioc);
-               bfa_ioc_hb_monitor(ioc);
                bfa_fsm_set_state(ioc, bfa_ioc_sm_op);
                break;
 
-       case IOC_E_FWRSP_ACQ_ADDR:
-               bfa_ioc_timer_stop(ioc);
-               bfa_ioc_hb_monitor(ioc);
-               bfa_fsm_set_state(ioc, bfa_ioc_sm_acq_addr);
-               break;
-
        case IOC_E_PFFAILED:
        case IOC_E_HWERROR:
                bfa_ioc_timer_stop(ioc);
@@ -406,51 +393,6 @@ bfa_ioc_sm_getattr(struct bfa_ioc_s *ioc, enum ioc_event event)
        }
 }
 
-/*
- * Acquiring address from fabric (entry function)
- */
-static void
-bfa_ioc_sm_acq_addr_entry(struct bfa_ioc_s *ioc)
-{
-}
-
-/*
- *     Acquiring address from the fabric
- */
-static void
-bfa_ioc_sm_acq_addr(struct bfa_ioc_s *ioc, enum ioc_event event)
-{
-       bfa_trc(ioc, event);
-
-       switch (event) {
-       case IOC_E_FWRSP_GETATTR:
-               bfa_ioc_check_attr_wwns(ioc);
-               bfa_fsm_set_state(ioc, bfa_ioc_sm_op);
-               break;
-
-       case IOC_E_PFFAILED:
-       case IOC_E_HWERROR:
-               bfa_hb_timer_stop(ioc);
-       case IOC_E_HBFAIL:
-               ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
-               bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
-               if (event != IOC_E_PFFAILED)
-                       bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_GETATTRFAIL);
-               break;
-
-       case IOC_E_DISABLE:
-               bfa_hb_timer_stop(ioc);
-               bfa_fsm_set_state(ioc, bfa_ioc_sm_disabling);
-               break;
-
-       case IOC_E_ENABLE:
-               break;
-
-       default:
-               bfa_sm_fault(ioc, event);
-       }
-}
-
 static void
 bfa_ioc_sm_op_entry(struct bfa_ioc_s *ioc)
 {
@@ -458,6 +400,7 @@ bfa_ioc_sm_op_entry(struct bfa_ioc_s *ioc)
 
        ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_OK);
        bfa_ioc_event_notify(ioc, BFA_IOC_E_ENABLED);
+       bfa_ioc_hb_monitor(ioc);
        BFA_LOG(KERN_INFO, bfad, bfa_log_level, "IOC enabled\n");
        bfa_ioc_aen_post(ioc, BFA_IOC_AEN_ENABLE);
 }
@@ -738,26 +681,60 @@ static void
 bfa_iocpf_sm_fwcheck_entry(struct bfa_iocpf_s *iocpf)
 {
        struct bfi_ioc_image_hdr_s      fwhdr;
-       u32     fwstate = readl(iocpf->ioc->ioc_regs.ioc_fwstate);
+       u32     r32, fwstate, pgnum, pgoff, loff = 0;
+       int     i;
+
+       /*
+        * Spin on init semaphore to serialize.
+        */
+       r32 = readl(iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+       while (r32 & 0x1) {
+               udelay(20);
+               r32 = readl(iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+       }
 
        /* h/w sem init */
-       if (fwstate == BFI_IOC_UNINIT)
+       fwstate = readl(iocpf->ioc->ioc_regs.ioc_fwstate);
+       if (fwstate == BFI_IOC_UNINIT) {
+               writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
                goto sem_get;
+       }
 
        bfa_ioc_fwver_get(iocpf->ioc, &fwhdr);
 
-       if (swab32(fwhdr.exec) == BFI_FWBOOT_TYPE_NORMAL)
+       if (swab32(fwhdr.exec) == BFI_FWBOOT_TYPE_NORMAL) {
+               writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
                goto sem_get;
+       }
+
+       /*
+        * Clear fwver hdr
+        */
+       pgnum = PSS_SMEM_PGNUM(iocpf->ioc->ioc_regs.smem_pg0, loff);
+       pgoff = PSS_SMEM_PGOFF(loff);
+       writel(pgnum, iocpf->ioc->ioc_regs.host_page_num_fn);
+
+       for (i = 0; i < sizeof(struct bfi_ioc_image_hdr_s) / sizeof(u32); i++) {
+               bfa_mem_write(iocpf->ioc->ioc_regs.smem_page_start, loff, 0);
+               loff += sizeof(u32);
+       }
 
        bfa_trc(iocpf->ioc, fwstate);
-       bfa_trc(iocpf->ioc, fwhdr.exec);
+       bfa_trc(iocpf->ioc, swab32(fwhdr.exec));
        writel(BFI_IOC_UNINIT, iocpf->ioc->ioc_regs.ioc_fwstate);
+       writel(BFI_IOC_UNINIT, iocpf->ioc->ioc_regs.alt_ioc_fwstate);
 
        /*
-        * Try to lock and then unlock the semaphore.
+        * Unlock the hw semaphore. Should be here only once per boot.
         */
        readl(iocpf->ioc->ioc_regs.ioc_sem_reg);
        writel(1, iocpf->ioc->ioc_regs.ioc_sem_reg);
+
+       /*
+        * unlock init semaphore.
+        */
+       writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+
 sem_get:
        bfa_ioc_hw_sem_get(iocpf->ioc);
 }
@@ -1707,11 +1684,6 @@ bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type,
        u32 i;
        u32 asicmode;
 
-       /*
-        * Initialize LMEM first before code download
-        */
-       bfa_ioc_lmem_init(ioc);
-
        bfa_trc(ioc, bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc)));
        fwimg = bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), chunkno);
 
@@ -1999,6 +1971,12 @@ bfa_ioc_pll_init(struct bfa_ioc_s *ioc)
        bfa_ioc_pll_init_asic(ioc);
 
        ioc->pllinit = BFA_TRUE;
+
+       /*
+        * Initialize LMEM
+        */
+       bfa_ioc_lmem_init(ioc);
+
        /*
         *  release semaphore.
         */
@@ -2122,10 +2100,6 @@ bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *m)
                bfa_ioc_getattr_reply(ioc);
                break;
 
-       case BFI_IOC_I2H_ACQ_ADDR_REPLY:
-               bfa_fsm_send_event(ioc, IOC_E_FWRSP_ACQ_ADDR);
-               break;
-
        default:
                bfa_trc(ioc, msg->mh.msg_id);
                WARN_ON(1);
@@ -2415,15 +2389,6 @@ bfa_ioc_is_disabled(struct bfa_ioc_s *ioc)
                bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled);
 }
 
-/*
- * Return TRUE if IOC is in acquiring address state
- */
-bfa_boolean_t
-bfa_ioc_is_acq_addr(struct bfa_ioc_s *ioc)
-{
-       return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_acq_addr);
-}
-
 /*
  * return true if IOC firmware is different.
  */
@@ -2916,17 +2881,6 @@ bfa_ioc_recover(struct bfa_ioc_s *ioc)
        bfa_fsm_send_event(ioc, IOC_E_HBFAIL);
 }
 
-static void
-bfa_ioc_check_attr_wwns(struct bfa_ioc_s *ioc)
-{
-       if (bfa_ioc_get_type(ioc) == BFA_IOC_TYPE_LL)
-               return;
-       if (ioc->attr->nwwn == 0)
-               bfa_ioc_aen_post(ioc, BFA_IOC_AEN_INVALID_NWWN);
-       if (ioc->attr->pwwn == 0)
-               bfa_ioc_aen_post(ioc, BFA_IOC_AEN_INVALID_PWWN);
-}
-
 /*
  *  BFA IOC PF private functions
  */
@@ -4495,7 +4449,7 @@ bfa_flash_read_part(struct bfa_flash_s *flash, enum bfa_flash_part_type type,
  */
 
 #define BFA_DIAG_MEMTEST_TOV   50000   /* memtest timeout in msec */
-#define BFA_DIAG_FWPING_TOV    1000    /* msec */
+#define CT2_BFA_DIAG_MEMTEST_TOV       (9*30*1000)  /* 4.5 min */
 
 /* IOC event handler */
 static void
@@ -4772,7 +4726,7 @@ diag_ledtest_send(struct bfa_diag_s *diag, struct bfa_diag_ledtest_s *ledtest)
 }
 
 static void
-diag_ledtest_comp(struct bfa_diag_s *diag, struct bfi_diag_ledtest_rsp_s * msg)
+diag_ledtest_comp(struct bfa_diag_s *diag, struct bfi_diag_ledtest_rsp_s *msg)
 {
        bfa_trc(diag, diag->ledtest.lock);
        diag->ledtest.lock = BFA_FALSE;
@@ -4850,6 +4804,8 @@ bfa_diag_memtest(struct bfa_diag_s *diag, struct bfa_diag_memtest_s *memtest,
                u32 pattern, struct bfa_diag_memtest_result *result,
                bfa_cb_diag_t cbfn, void *cbarg)
 {
+       u32     memtest_tov;
+
        bfa_trc(diag, pattern);
 
        if (!bfa_ioc_adapter_is_disabled(diag->ioc))
@@ -4869,8 +4825,10 @@ bfa_diag_memtest(struct bfa_diag_s *diag, struct bfa_diag_memtest_s *memtest,
        /* download memtest code and take LPU0 out of reset */
        bfa_ioc_boot(diag->ioc, BFI_FWBOOT_TYPE_MEMTEST, BFI_FWBOOT_ENV_OS);
 
+       memtest_tov = (bfa_ioc_asic_gen(diag->ioc) == BFI_ASIC_GEN_CT2) ?
+                      CT2_BFA_DIAG_MEMTEST_TOV : BFA_DIAG_MEMTEST_TOV;
        bfa_timer_begin(diag->ioc->timer_mod, &diag->timer,
-                       bfa_diag_memtest_done, diag, BFA_DIAG_MEMTEST_TOV);
+                       bfa_diag_memtest_done, diag, memtest_tov);
        diag->timer_active = 1;
        return BFA_STATUS_OK;
 }
@@ -5641,24 +5599,27 @@ bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
        case BFA_DCONF_SM_INIT:
                if (dconf->min_cfg) {
                        bfa_trc(dconf->bfa, dconf->min_cfg);
+                       bfa_fsm_send_event(&dconf->bfa->iocfc,
+                                       IOCFC_E_DCONF_DONE);
                        return;
                }
                bfa_sm_set_state(dconf, bfa_dconf_sm_flash_read);
-               dconf->flashdone = BFA_FALSE;
-               bfa_trc(dconf->bfa, dconf->flashdone);
+               bfa_timer_start(dconf->bfa, &dconf->timer,
+                       bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
                bfa_status = bfa_flash_read_part(BFA_FLASH(dconf->bfa),
                                        BFA_FLASH_PART_DRV, dconf->instance,
                                        dconf->dconf,
                                        sizeof(struct bfa_dconf_s), 0,
                                        bfa_dconf_init_cb, dconf->bfa);
                if (bfa_status != BFA_STATUS_OK) {
+                       bfa_timer_stop(&dconf->timer);
                        bfa_dconf_init_cb(dconf->bfa, BFA_STATUS_FAILED);
                        bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
                        return;
                }
                break;
        case BFA_DCONF_SM_EXIT:
-               dconf->flashdone = BFA_TRUE;
+               bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
        case BFA_DCONF_SM_IOCDISABLE:
        case BFA_DCONF_SM_WR:
        case BFA_DCONF_SM_FLASH_COMP:
@@ -5679,15 +5640,20 @@ bfa_dconf_sm_flash_read(struct bfa_dconf_mod_s *dconf,
 
        switch (event) {
        case BFA_DCONF_SM_FLASH_COMP:
+               bfa_timer_stop(&dconf->timer);
                bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
                break;
        case BFA_DCONF_SM_TIMEOUT:
                bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+               bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_IOC_FAILED);
                break;
        case BFA_DCONF_SM_EXIT:
-               dconf->flashdone = BFA_TRUE;
-               bfa_trc(dconf->bfa, dconf->flashdone);
+               bfa_timer_stop(&dconf->timer);
+               bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+               bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
+               break;
        case BFA_DCONF_SM_IOCDISABLE:
+               bfa_timer_stop(&dconf->timer);
                bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
                break;
        default:
@@ -5710,9 +5676,8 @@ bfa_dconf_sm_ready(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
                bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
                break;
        case BFA_DCONF_SM_EXIT:
-               dconf->flashdone = BFA_TRUE;
-               bfa_trc(dconf->bfa, dconf->flashdone);
                bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+               bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
                break;
        case BFA_DCONF_SM_INIT:
        case BFA_DCONF_SM_IOCDISABLE:
@@ -5774,9 +5739,7 @@ bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf,
                bfa_timer_stop(&dconf->timer);
        case BFA_DCONF_SM_TIMEOUT:
                bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
-               dconf->flashdone = BFA_TRUE;
-               bfa_trc(dconf->bfa, dconf->flashdone);
-               bfa_ioc_disable(&dconf->bfa->ioc);
+               bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
                break;
        default:
                bfa_sm_fault(dconf->bfa, event);
@@ -5823,8 +5786,8 @@ bfa_dconf_sm_iocdown_dirty(struct bfa_dconf_mod_s *dconf,
                bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
                break;
        case BFA_DCONF_SM_EXIT:
-               dconf->flashdone = BFA_TRUE;
                bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+               bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
                break;
        case BFA_DCONF_SM_IOCDISABLE:
                break;
@@ -5865,11 +5828,6 @@ bfa_dconf_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
        if (cfg->drvcfg.min_cfg) {
                bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_hdr_s);
                dconf->min_cfg = BFA_TRUE;
-               /*
-                * Set the flashdone flag to TRUE explicitly as no flash
-                * write will happen in min_cfg mode.
-                */
-               dconf->flashdone = BFA_TRUE;
        } else {
                dconf->min_cfg = BFA_FALSE;
                bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_s);
@@ -5885,9 +5843,7 @@ bfa_dconf_init_cb(void *arg, bfa_status_t status)
        struct bfa_s *bfa = arg;
        struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
 
-       dconf->flashdone = BFA_TRUE;
-       bfa_trc(bfa, dconf->flashdone);
-       bfa_iocfc_cb_dconf_modinit(bfa, status);
+       bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
        if (status == BFA_STATUS_OK) {
                bfa_dconf_read_data_valid(bfa) = BFA_TRUE;
                if (dconf->dconf->hdr.signature != BFI_DCONF_SIGNATURE)
@@ -5895,7 +5851,7 @@ bfa_dconf_init_cb(void *arg, bfa_status_t status)
                if (dconf->dconf->hdr.version != BFI_DCONF_VERSION)
                        dconf->dconf->hdr.version = BFI_DCONF_VERSION;
        }
-       bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
+       bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_DCONF_DONE);
 }
 
 void
@@ -5977,7 +5933,5 @@ void
 bfa_dconf_modexit(struct bfa_s *bfa)
 {
        struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
-       BFA_DCONF_MOD(bfa)->flashdone = BFA_FALSE;
-       bfa_trc(bfa, BFA_DCONF_MOD(bfa)->flashdone);
        bfa_sm_send_event(dconf, BFA_DCONF_SM_EXIT);
 }
index 546d46b371017102136b0d5b87d49347b424755c..1a99d4b5b50feec22d97c2e87430c317589877e5 100644 (file)
@@ -372,6 +372,22 @@ struct bfa_cb_qe_s {
        void            *cbarg;
 };
 
+/*
+ * IOCFC state machine definitions/declarations
+ */
+enum iocfc_event {
+       IOCFC_E_INIT            = 1,    /* IOCFC init request           */
+       IOCFC_E_START           = 2,    /* IOCFC mod start request      */
+       IOCFC_E_STOP            = 3,    /* IOCFC stop request           */
+       IOCFC_E_ENABLE          = 4,    /* IOCFC enable request         */
+       IOCFC_E_DISABLE         = 5,    /* IOCFC disable request        */
+       IOCFC_E_IOC_ENABLED     = 6,    /* IOC enabled message          */
+       IOCFC_E_IOC_DISABLED    = 7,    /* IOC disabled message         */
+       IOCFC_E_IOC_FAILED      = 8,    /* failure notice by IOC sm     */
+       IOCFC_E_DCONF_DONE      = 9,    /* dconf read/write done        */
+       IOCFC_E_CFG_DONE        = 10,   /* IOCFC config complete        */
+};
+
 /*
  * ASIC block configurtion related
  */
@@ -706,7 +722,6 @@ struct bfa_dconf_s {
 struct bfa_dconf_mod_s {
        bfa_sm_t                sm;
        u8                      instance;
-       bfa_boolean_t           flashdone;
        bfa_boolean_t           read_data_valid;
        bfa_boolean_t           min_cfg;
        struct bfa_timer_s      timer;
index d1b8f0caaa79ed3f301d6c8b509339fee76d0169..2eb0c6a2938d68a9276c4f19039445cc3ec93fdd 100644 (file)
@@ -786,17 +786,73 @@ bfa_ioc_ct2_mac_reset(void __iomem *rb)
 }
 
 #define CT2_NFC_MAX_DELAY      1000
+#define CT2_NFC_VER_VALID      0x143
+#define BFA_IOC_PLL_POLL       1000000
+
+static bfa_boolean_t
+bfa_ioc_ct2_nfc_halted(void __iomem *rb)
+{
+       u32     r32;
+
+       r32 = readl(rb + CT2_NFC_CSR_SET_REG);
+       if (r32 & __NFC_CONTROLLER_HALTED)
+               return BFA_TRUE;
+
+       return BFA_FALSE;
+}
+
+static void
+bfa_ioc_ct2_nfc_resume(void __iomem *rb)
+{
+       u32     r32;
+       int i;
+
+       writel(__HALT_NFC_CONTROLLER, rb + CT2_NFC_CSR_CLR_REG);
+       for (i = 0; i < CT2_NFC_MAX_DELAY; i++) {
+               r32 = readl(rb + CT2_NFC_CSR_SET_REG);
+               if (!(r32 & __NFC_CONTROLLER_HALTED))
+                       return;
+               udelay(1000);
+       }
+       WARN_ON(1);
+}
+
 bfa_status_t
 bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode mode)
 {
-       u32     wgn, r32;
-       int i;
+       u32 wgn, r32, nfc_ver, i;
 
-       /*
-        * Initialize PLL if not already done by NFC
-        */
        wgn = readl(rb + CT2_WGN_STATUS);
-       if (!(wgn & __GLBL_PF_VF_CFG_RDY)) {
+       nfc_ver = readl(rb + CT2_RSC_GPR15_REG);
+
+       if ((wgn == (__A2T_AHB_LOAD | __WGN_READY)) &&
+           (nfc_ver >= CT2_NFC_VER_VALID)) {
+               if (bfa_ioc_ct2_nfc_halted(rb))
+                       bfa_ioc_ct2_nfc_resume(rb);
+
+               writel(__RESET_AND_START_SCLK_LCLK_PLLS,
+                      rb + CT2_CSI_FW_CTL_SET_REG);
+
+               for (i = 0; i < BFA_IOC_PLL_POLL; i++) {
+                       r32 = readl(rb + CT2_APP_PLL_LCLK_CTL_REG);
+                       if (r32 & __RESET_AND_START_SCLK_LCLK_PLLS)
+                               break;
+               }
+
+               WARN_ON(!(r32 & __RESET_AND_START_SCLK_LCLK_PLLS));
+
+               for (i = 0; i < BFA_IOC_PLL_POLL; i++) {
+                       r32 = readl(rb + CT2_APP_PLL_LCLK_CTL_REG);
+                       if (!(r32 & __RESET_AND_START_SCLK_LCLK_PLLS))
+                               break;
+               }
+
+               WARN_ON(r32 & __RESET_AND_START_SCLK_LCLK_PLLS);
+               udelay(1000);
+
+               r32 = readl(rb + CT2_CSI_FW_CTL_REG);
+               WARN_ON(r32 & __RESET_AND_START_SCLK_LCLK_PLLS);
+       } else {
                writel(__HALT_NFC_CONTROLLER, rb + CT2_NFC_CSR_SET_REG);
                for (i = 0; i < CT2_NFC_MAX_DELAY; i++) {
                        r32 = readl(rb + CT2_NFC_CSR_SET_REG);
@@ -804,57 +860,62 @@ bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode mode)
                                break;
                        udelay(1000);
                }
-       }
 
-       /*
-        * Mask the interrupts and clear any
-        * pending interrupts.
-        */
-       writel(1, (rb + CT2_LPU0_HOSTFN_MBOX0_MSK));
-       writel(1, (rb + CT2_LPU1_HOSTFN_MBOX0_MSK));
-
-       r32 = readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
-       if (r32 == 1) {
-               writel(1, (rb + CT2_LPU0_HOSTFN_CMD_STAT));
-               readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
+               bfa_ioc_ct2_mac_reset(rb);
+               bfa_ioc_ct2_sclk_init(rb);
+               bfa_ioc_ct2_lclk_init(rb);
+
+               /*
+                * release soft reset on s_clk & l_clk
+                */
+               r32 = readl(rb + CT2_APP_PLL_SCLK_CTL_REG);
+               writel(r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET,
+                      (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+               /*
+                * release soft reset on s_clk & l_clk
+                */
+               r32 = readl(rb + CT2_APP_PLL_LCLK_CTL_REG);
+               writel(r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET,
+                     (rb + CT2_APP_PLL_LCLK_CTL_REG));
        }
-       r32 = readl((rb + CT2_LPU1_HOSTFN_CMD_STAT));
-       if (r32 == 1) {
-               writel(1, (rb + CT2_LPU1_HOSTFN_CMD_STAT));
-               readl((rb + CT2_LPU1_HOSTFN_CMD_STAT));
-       }
-
-       bfa_ioc_ct2_mac_reset(rb);
-       bfa_ioc_ct2_sclk_init(rb);
-       bfa_ioc_ct2_lclk_init(rb);
-
-       /*
-        * release soft reset on s_clk & l_clk
-        */
-       r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
-       writel(r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET,
-               (rb + CT2_APP_PLL_SCLK_CTL_REG));
-
-       /*
-        * release soft reset on s_clk & l_clk
-        */
-       r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
-       writel(r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET,
-               (rb + CT2_APP_PLL_LCLK_CTL_REG));
 
        /*
         * Announce flash device presence, if flash was corrupted.
         */
        if (wgn == (__WGN_READY | __GLBL_PF_VF_CFG_RDY)) {
-               r32 = readl((rb + PSS_GPIO_OUT_REG));
+               r32 = readl(rb + PSS_GPIO_OUT_REG);
                writel(r32 & ~1, (rb + PSS_GPIO_OUT_REG));
-               r32 = readl((rb + PSS_GPIO_OE_REG));
+               r32 = readl(rb + PSS_GPIO_OE_REG);
                writel(r32 | 1, (rb + PSS_GPIO_OE_REG));
        }
 
+       /*
+        * Mask the interrupts and clear any
+        * pending interrupts.
+        */
+       writel(1, (rb + CT2_LPU0_HOSTFN_MBOX0_MSK));
+       writel(1, (rb + CT2_LPU1_HOSTFN_MBOX0_MSK));
+
+       /* For first time initialization, no need to clear interrupts */
+       r32 = readl(rb + HOST_SEM5_REG);
+       if (r32 & 0x1) {
+               r32 = readl(rb + CT2_LPU0_HOSTFN_CMD_STAT);
+               if (r32 == 1) {
+                       writel(1, rb + CT2_LPU0_HOSTFN_CMD_STAT);
+                       readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
+               }
+               r32 = readl(rb + CT2_LPU1_HOSTFN_CMD_STAT);
+               if (r32 == 1) {
+                       writel(1, rb + CT2_LPU1_HOSTFN_CMD_STAT);
+                       readl(rb + CT2_LPU1_HOSTFN_CMD_STAT);
+               }
+       }
+
        bfa_ioc_ct2_mem_init(rb);
 
-       writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC0_STATE_REG));
-       writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC1_STATE_REG));
+       writel(BFI_IOC_UNINIT, rb + CT2_BFA_IOC0_STATE_REG);
+       writel(BFI_IOC_UNINIT, rb + CT2_BFA_IOC1_STATE_REG);
+
        return BFA_STATUS_OK;
 }
index aa8a0eaf91f9c6b7a2e846dc51048726dd10c5a2..2e856e6710f7d10b2e7083771cdac865bfd15404 100644 (file)
@@ -1280,6 +1280,7 @@ bfa_lps_sm_loginwait(struct bfa_lps_s *lps, enum bfa_lps_event event)
        switch (event) {
        case BFA_LPS_SM_RESUME:
                bfa_sm_set_state(lps, bfa_lps_sm_login);
+               bfa_lps_send_login(lps);
                break;
 
        case BFA_LPS_SM_OFFLINE:
@@ -1578,7 +1579,7 @@ bfa_lps_login_rsp(struct bfa_s *bfa, struct bfi_lps_login_rsp_s *rsp)
                break;
 
        case BFA_STATUS_VPORT_MAX:
-               if (!rsp->ext_status)
+               if (rsp->ext_status)
                        bfa_lps_no_res(lps, rsp->ext_status);
                break;
 
@@ -3083,33 +3084,6 @@ bfa_fcport_set_wwns(struct bfa_fcport_s *fcport)
        bfa_trc(fcport->bfa, fcport->nwwn);
 }
 
-static void
-bfa_fcport_send_txcredit(void *port_cbarg)
-{
-
-       struct bfa_fcport_s *fcport = port_cbarg;
-       struct bfi_fcport_set_svc_params_req_s *m;
-
-       /*
-        * check for room in queue to send request now
-        */
-       m = bfa_reqq_next(fcport->bfa, BFA_REQQ_PORT);
-       if (!m) {
-               bfa_trc(fcport->bfa, fcport->cfg.tx_bbcredit);
-               return;
-       }
-
-       bfi_h2i_set(m->mh, BFI_MC_FCPORT, BFI_FCPORT_H2I_SET_SVC_PARAMS_REQ,
-                       bfa_fn_lpu(fcport->bfa));
-       m->tx_bbcredit = cpu_to_be16((u16)fcport->cfg.tx_bbcredit);
-       m->bb_scn = fcport->cfg.bb_scn;
-
-       /*
-        * queue I/O message to firmware
-        */
-       bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT, m->mh);
-}
-
 static void
 bfa_fcport_qos_stats_swap(struct bfa_qos_stats_s *d,
        struct bfa_qos_stats_s *s)
@@ -3602,26 +3576,24 @@ bfa_fcport_cfg_speed(struct bfa_s *bfa, enum bfa_port_speed speed)
                return BFA_STATUS_UNSUPP_SPEED;
        }
 
-       /* For Mezz card, port speed entered needs to be checked */
-       if (bfa_mfg_is_mezz(fcport->bfa->ioc.attr->card_type)) {
-               if (bfa_ioc_get_type(&fcport->bfa->ioc) == BFA_IOC_TYPE_FC) {
-                       /* For CT2, 1G is not supported */
-                       if ((speed == BFA_PORT_SPEED_1GBPS) &&
-                           (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id)))
-                               return BFA_STATUS_UNSUPP_SPEED;
+       /* Port speed entered needs to be checked */
+       if (bfa_ioc_get_type(&fcport->bfa->ioc) == BFA_IOC_TYPE_FC) {
+               /* For CT2, 1G is not supported */
+               if ((speed == BFA_PORT_SPEED_1GBPS) &&
+                   (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id)))
+                       return BFA_STATUS_UNSUPP_SPEED;
 
-                       /* Already checked for Auto Speed and Max Speed supp */
-                       if (!(speed == BFA_PORT_SPEED_1GBPS ||
-                             speed == BFA_PORT_SPEED_2GBPS ||
-                             speed == BFA_PORT_SPEED_4GBPS ||
-                             speed == BFA_PORT_SPEED_8GBPS ||
-                             speed == BFA_PORT_SPEED_16GBPS ||
-                             speed == BFA_PORT_SPEED_AUTO))
-                               return BFA_STATUS_UNSUPP_SPEED;
-               } else {
-                       if (speed != BFA_PORT_SPEED_10GBPS)
-                               return BFA_STATUS_UNSUPP_SPEED;
-               }
+               /* Already checked for Auto Speed and Max Speed supp */
+               if (!(speed == BFA_PORT_SPEED_1GBPS ||
+                     speed == BFA_PORT_SPEED_2GBPS ||
+                     speed == BFA_PORT_SPEED_4GBPS ||
+                     speed == BFA_PORT_SPEED_8GBPS ||
+                     speed == BFA_PORT_SPEED_16GBPS ||
+                     speed == BFA_PORT_SPEED_AUTO))
+                       return BFA_STATUS_UNSUPP_SPEED;
+       } else {
+               if (speed != BFA_PORT_SPEED_10GBPS)
+                       return BFA_STATUS_UNSUPP_SPEED;
        }
 
        fcport->cfg.speed = speed;
@@ -3765,7 +3737,6 @@ bfa_fcport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit, u8 bb_scn)
        fcport->cfg.bb_scn = bb_scn;
        if (bb_scn)
                fcport->bbsc_op_state = BFA_TRUE;
-       bfa_fcport_send_txcredit(fcport);
 }
 
 /*
@@ -3825,8 +3796,6 @@ bfa_fcport_get_attr(struct bfa_s *bfa, struct bfa_port_attr_s *attr)
                        attr->port_state = BFA_PORT_ST_IOCDIS;
                else if (bfa_ioc_fw_mismatch(&fcport->bfa->ioc))
                        attr->port_state = BFA_PORT_ST_FWMISMATCH;
-               else if (bfa_ioc_is_acq_addr(&fcport->bfa->ioc))
-                       attr->port_state = BFA_PORT_ST_ACQ_ADDR;
        }
 
        /* FCoE vlan */
index b52cbb6bcd5a3b6b4c7623753df79630892686ea..f300675646395b8702d8879cde778cddb08755e1 100644 (file)
@@ -663,10 +663,6 @@ void bfa_cb_lps_fdisclogo_comp(void *bfad, void *uarg);
 void bfa_cb_lps_cvl_event(void *bfad, void *uarg);
 
 /* FAA specific APIs */
-bfa_status_t bfa_faa_enable(struct bfa_s *bfa,
-                       bfa_cb_iocfc_t cbfn, void *cbarg);
-bfa_status_t bfa_faa_disable(struct bfa_s *bfa,
-                       bfa_cb_iocfc_t cbfn, void *cbarg);
 bfa_status_t bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
                        bfa_cb_iocfc_t cbfn, void *cbarg);
 
index 1938fe0473e99b9aa24a5ee6e50a4e6fe9e9ac4d..7b1ecd2b3ffe8b382689429c6e91d01ae04e45a1 100644 (file)
@@ -442,6 +442,43 @@ bfad_im_vport_create(struct fc_vport *fc_vport, bool disable)
        return status;
 }
 
+int
+bfad_im_issue_fc_host_lip(struct Scsi_Host *shost)
+{
+       struct bfad_im_port_s *im_port =
+                       (struct bfad_im_port_s *) shost->hostdata[0];
+       struct bfad_s *bfad = im_port->bfad;
+       struct bfad_hal_comp fcomp;
+       unsigned long flags;
+       uint32_t status;
+
+       init_completion(&fcomp.comp);
+       spin_lock_irqsave(&bfad->bfad_lock, flags);
+       status = bfa_port_disable(&bfad->bfa.modules.port,
+                                       bfad_hcb_comp, &fcomp);
+       spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+       if (status != BFA_STATUS_OK)
+               return -EIO;
+
+       wait_for_completion(&fcomp.comp);
+       if (fcomp.status != BFA_STATUS_OK)
+               return -EIO;
+
+       spin_lock_irqsave(&bfad->bfad_lock, flags);
+       status = bfa_port_enable(&bfad->bfa.modules.port,
+                                       bfad_hcb_comp, &fcomp);
+       spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+       if (status != BFA_STATUS_OK)
+               return -EIO;
+
+       wait_for_completion(&fcomp.comp);
+       if (fcomp.status != BFA_STATUS_OK)
+               return -EIO;
+
+       return 0;
+}
+
 static int
 bfad_im_vport_delete(struct fc_vport *fc_vport)
 {
@@ -457,8 +494,11 @@ bfad_im_vport_delete(struct fc_vport *fc_vport)
        unsigned long flags;
        struct completion fcomp;
 
-       if (im_port->flags & BFAD_PORT_DELETE)
-               goto free_scsi_host;
+       if (im_port->flags & BFAD_PORT_DELETE) {
+               bfad_scsi_host_free(bfad, im_port);
+               list_del(&vport->list_entry);
+               return 0;
+       }
 
        port = im_port->port;
 
@@ -489,7 +529,6 @@ bfad_im_vport_delete(struct fc_vport *fc_vport)
 
        wait_for_completion(vport->comp_del);
 
-free_scsi_host:
        bfad_scsi_host_free(bfad, im_port);
        list_del(&vport->list_entry);
        kfree(vport);
@@ -579,7 +618,7 @@ struct fc_function_template bfad_im_fc_function_template = {
        .show_rport_dev_loss_tmo = 1,
        .get_rport_dev_loss_tmo = bfad_im_get_rport_loss_tmo,
        .set_rport_dev_loss_tmo = bfad_im_set_rport_loss_tmo,
-
+       .issue_fc_host_lip = bfad_im_issue_fc_host_lip,
        .vport_create = bfad_im_vport_create,
        .vport_delete = bfad_im_vport_delete,
        .vport_disable = bfad_im_vport_disable,
index 8005c6c5a080efa3b9e2b2b2b6403a1323cee9e1..e1f4b10df42aaf14cb9d069c9ddc372393c473c3 100644 (file)
@@ -1287,50 +1287,6 @@ out:
        return 0;
 }
 
-int
-bfad_iocmd_faa_enable(struct bfad_s *bfad, void *cmd)
-{
-       struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
-       unsigned long   flags;
-       struct bfad_hal_comp    fcomp;
-
-       init_completion(&fcomp.comp);
-       iocmd->status = BFA_STATUS_OK;
-       spin_lock_irqsave(&bfad->bfad_lock, flags);
-       iocmd->status = bfa_faa_enable(&bfad->bfa, bfad_hcb_comp, &fcomp);
-       spin_unlock_irqrestore(&bfad->bfad_lock, flags);
-
-       if (iocmd->status != BFA_STATUS_OK)
-               goto out;
-
-       wait_for_completion(&fcomp.comp);
-       iocmd->status = fcomp.status;
-out:
-       return 0;
-}
-
-int
-bfad_iocmd_faa_disable(struct bfad_s *bfad, void *cmd)
-{
-       struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
-       unsigned long   flags;
-       struct bfad_hal_comp    fcomp;
-
-       init_completion(&fcomp.comp);
-       iocmd->status = BFA_STATUS_OK;
-       spin_lock_irqsave(&bfad->bfad_lock, flags);
-       iocmd->status = bfa_faa_disable(&bfad->bfa, bfad_hcb_comp, &fcomp);
-       spin_unlock_irqrestore(&bfad->bfad_lock, flags);
-
-       if (iocmd->status != BFA_STATUS_OK)
-               goto out;
-
-       wait_for_completion(&fcomp.comp);
-       iocmd->status = fcomp.status;
-out:
-       return 0;
-}
-
 int
 bfad_iocmd_faa_query(struct bfad_s *bfad, void *cmd)
 {
@@ -1918,6 +1874,7 @@ bfad_iocmd_debug_fw_core(struct bfad_s *bfad, void *cmd,
        struct bfa_bsg_debug_s *iocmd = (struct bfa_bsg_debug_s *)cmd;
        void    *iocmd_bufptr;
        unsigned long   flags;
+       u32 offset;
 
        if (bfad_chk_iocmd_sz(payload_len, sizeof(struct bfa_bsg_debug_s),
                        BFA_DEBUG_FW_CORE_CHUNK_SZ) != BFA_STATUS_OK) {
@@ -1935,8 +1892,10 @@ bfad_iocmd_debug_fw_core(struct bfad_s *bfad, void *cmd,
 
        iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_debug_s);
        spin_lock_irqsave(&bfad->bfad_lock, flags);
+       offset = iocmd->offset;
        iocmd->status = bfa_ioc_debug_fwcore(&bfad->bfa.ioc, iocmd_bufptr,
-                               (u32 *)&iocmd->offset, &iocmd->bufsz);
+                               &offset, &iocmd->bufsz);
+       iocmd->offset = offset;
        spin_unlock_irqrestore(&bfad->bfad_lock, flags);
 out:
        return 0;
@@ -2633,12 +2592,6 @@ bfad_iocmd_handler(struct bfad_s *bfad, unsigned int cmd, void *iocmd,
        case IOCMD_FLASH_DISABLE_OPTROM:
                rc = bfad_iocmd_ablk_optrom(bfad, cmd, iocmd);
                break;
-       case IOCMD_FAA_ENABLE:
-               rc = bfad_iocmd_faa_enable(bfad, iocmd);
-               break;
-       case IOCMD_FAA_DISABLE:
-               rc = bfad_iocmd_faa_disable(bfad, iocmd);
-               break;
        case IOCMD_FAA_QUERY:
                rc = bfad_iocmd_faa_query(bfad, iocmd);
                break;
@@ -2809,9 +2762,16 @@ bfad_im_bsg_vendor_request(struct fc_bsg_job *job)
        struct bfad_im_port_s *im_port =
                        (struct bfad_im_port_s *) job->shost->hostdata[0];
        struct bfad_s *bfad = im_port->bfad;
+       struct request_queue *request_q = job->req->q;
        void *payload_kbuf;
        int rc = -EINVAL;
 
+       /*
+        * Set the BSG device request_queue size to 256 to support
+        * payloads larger than 512*1024K bytes.
+        */
+       blk_queue_max_segments(request_q, 256);
+
        /* Allocate a temp buffer to hold the passed in user space command */
        payload_kbuf = kzalloc(job->request_payload.payload_len, GFP_KERNEL);
        if (!payload_kbuf) {
index e859adb9aa9e807ae779e2ddb5444ec31bc7400b..17ad67283130d1f1528ade5e97893766e6534eac 100644 (file)
@@ -83,8 +83,6 @@ enum {
        IOCMD_PORT_CFG_MODE,
        IOCMD_FLASH_ENABLE_OPTROM,
        IOCMD_FLASH_DISABLE_OPTROM,
-       IOCMD_FAA_ENABLE,
-       IOCMD_FAA_DISABLE,
        IOCMD_FAA_QUERY,
        IOCMD_CEE_GET_ATTR,
        IOCMD_CEE_GET_STATS,
index dc5b9d99c4505f1356f7e0bed16d05bd992285ef..7f74f1d19124a95ea4f39f4c1110bb4fbf93e50c 100644 (file)
@@ -56,7 +56,7 @@
 #ifdef BFA_DRIVER_VERSION
 #define BFAD_DRIVER_VERSION    BFA_DRIVER_VERSION
 #else
-#define BFAD_DRIVER_VERSION    "3.0.2.2"
+#define BFAD_DRIVER_VERSION    "3.0.23.0"
 #endif
 
 #define BFAD_PROTO_NAME FCPI_NAME
index 0d9f1fb50db0c0ae74ea3e5e7a529b0594f436a9..d4220e13cafa09a4d8e90193e75514f691f596be 100644 (file)
@@ -28,17 +28,15 @@ enum bfi_iocfc_h2i_msgs {
        BFI_IOCFC_H2I_CFG_REQ           = 1,
        BFI_IOCFC_H2I_SET_INTR_REQ      = 2,
        BFI_IOCFC_H2I_UPDATEQ_REQ       = 3,
-       BFI_IOCFC_H2I_FAA_ENABLE_REQ    = 4,
-       BFI_IOCFC_H2I_FAA_DISABLE_REQ   = 5,
-       BFI_IOCFC_H2I_FAA_QUERY_REQ     = 6,
+       BFI_IOCFC_H2I_FAA_QUERY_REQ     = 4,
+       BFI_IOCFC_H2I_ADDR_REQ          = 5,
 };
 
 enum bfi_iocfc_i2h_msgs {
        BFI_IOCFC_I2H_CFG_REPLY         = BFA_I2HM(1),
        BFI_IOCFC_I2H_UPDATEQ_RSP       = BFA_I2HM(3),
-       BFI_IOCFC_I2H_FAA_ENABLE_RSP    = BFA_I2HM(4),
-       BFI_IOCFC_I2H_FAA_DISABLE_RSP   = BFA_I2HM(5),
-       BFI_IOCFC_I2H_FAA_QUERY_RSP     = BFA_I2HM(6),
+       BFI_IOCFC_I2H_FAA_QUERY_RSP     = BFA_I2HM(4),
+       BFI_IOCFC_I2H_ADDR_MSG          = BFA_I2HM(5),
 };
 
 struct bfi_iocfc_cfg_s {
@@ -184,6 +182,13 @@ struct bfi_faa_en_dis_s {
        struct bfi_mhdr_s mh;   /* common msg header    */
 };
 
+struct bfi_faa_addr_msg_s {
+       struct  bfi_mhdr_s mh;  /* common msg header    */
+       u8      rsvd[4];
+       wwn_t   pwwn;           /* Fabric acquired PWWN */
+       wwn_t   nwwn;           /* Fabric acquired PWWN */
+};
+
 /*
  * BFI_IOCFC_H2I_FAA_QUERY_REQ message
  */
index d892064b64a8b42f848e3d8b5a134348075215b3..ed5f159e18671383569522922d6cb9134849c0e2 100644 (file)
@@ -335,11 +335,17 @@ enum {
 #define __PMM_1T_PNDB_P                        0x00000002
 #define CT2_PMM_1T_CONTROL_REG_P1      0x00023c1c
 #define CT2_WGN_STATUS                 0x00014990
+#define __A2T_AHB_LOAD                 0x00000800
 #define __WGN_READY                    0x00000400
 #define __GLBL_PF_VF_CFG_RDY           0x00000200
+#define CT2_NFC_CSR_CLR_REG            0x00027420
 #define CT2_NFC_CSR_SET_REG            0x00027424
 #define __HALT_NFC_CONTROLLER          0x00000002
 #define __NFC_CONTROLLER_HALTED                0x00001000
+#define CT2_RSC_GPR15_REG              0x0002765c
+#define CT2_CSI_FW_CTL_REG             0x00027080
+#define CT2_CSI_FW_CTL_SET_REG         0x00027088
+#define __RESET_AND_START_SCLK_LCLK_PLLS 0x00010000
 
 #define CT2_CSI_MAC0_CONTROL_REG       0x000270d0
 #define __CSI_MAC_RESET                        0x00000010
index abd72a01856d514ddd0918730e59d998edd9f487..c1c6a92a0b989737c9f8a15b81e8e24cd86b758e 100644 (file)
@@ -439,13 +439,13 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
        fr->fr_dev = lport;
 
        bg = &bnx2fc_global;
-       spin_lock_bh(&bg->fcoe_rx_list.lock);
+       spin_lock(&bg->fcoe_rx_list.lock);
 
        __skb_queue_tail(&bg->fcoe_rx_list, skb);
        if (bg->fcoe_rx_list.qlen == 1)
                wake_up_process(bg->thread);
 
-       spin_unlock_bh(&bg->fcoe_rx_list.lock);
+       spin_unlock(&bg->fcoe_rx_list.lock);
 
        return 0;
 err:
index ae7d15c44e2aec40f97e0ad8dd9b4d4716141c73..335e85192807a4b75e7aa229cc2b2b6f396e0b9b 100644 (file)
@@ -1436,7 +1436,7 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
                goto err;
 
        fps = &per_cpu(fcoe_percpu, cpu);
-       spin_lock_bh(&fps->fcoe_rx_list.lock);
+       spin_lock(&fps->fcoe_rx_list.lock);
        if (unlikely(!fps->thread)) {
                /*
                 * The targeted CPU is not ready, let's target
@@ -1447,12 +1447,12 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
                                "ready for incoming skb- using first online "
                                "CPU.\n");
 
-               spin_unlock_bh(&fps->fcoe_rx_list.lock);
+               spin_unlock(&fps->fcoe_rx_list.lock);
                cpu = cpumask_first(cpu_online_mask);
                fps = &per_cpu(fcoe_percpu, cpu);
-               spin_lock_bh(&fps->fcoe_rx_list.lock);
+               spin_lock(&fps->fcoe_rx_list.lock);
                if (!fps->thread) {
-                       spin_unlock_bh(&fps->fcoe_rx_list.lock);
+                       spin_unlock(&fps->fcoe_rx_list.lock);
                        goto err;
                }
        }
@@ -1463,24 +1463,17 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
         * so we're free to queue skbs into it's queue.
         */
 
-       /* If this is a SCSI-FCP frame, and this is already executing on the
-        * correct CPU, and the queue for this CPU is empty, then go ahead
-        * and process the frame directly in the softirq context.
-        * This lets us process completions without context switching from the
-        * NET_RX softirq, to our receive processing thread, and then back to
-        * BLOCK softirq context.
+       /*
+        * Note: We used to have a set of conditions under which we would
+        * call fcoe_recv_frame directly, rather than queuing to the rx list
+        * as it could save a few cycles, but doing so is prohibited, as
+        * fcoe_recv_frame has several paths that may sleep, which is forbidden
+        * in softirq context.
         */
-       if (fh->fh_type == FC_TYPE_FCP &&
-           cpu == smp_processor_id() &&
-           skb_queue_empty(&fps->fcoe_rx_list)) {
-               spin_unlock_bh(&fps->fcoe_rx_list.lock);
-               fcoe_recv_frame(skb);
-       } else {
-               __skb_queue_tail(&fps->fcoe_rx_list, skb);
-               if (fps->fcoe_rx_list.qlen == 1)
-                       wake_up_process(fps->thread);
-               spin_unlock_bh(&fps->fcoe_rx_list.lock);
-       }
+       __skb_queue_tail(&fps->fcoe_rx_list, skb);
+       if (fps->thread->state == TASK_INTERRUPTIBLE)
+               wake_up_process(fps->thread);
+       spin_unlock(&fps->fcoe_rx_list.lock);
 
        return 0;
 err:
@@ -1797,23 +1790,29 @@ static int fcoe_percpu_receive_thread(void *arg)
 {
        struct fcoe_percpu_s *p = arg;
        struct sk_buff *skb;
+       struct sk_buff_head tmp;
+
+       skb_queue_head_init(&tmp);
 
        set_user_nice(current, -20);
 
        while (!kthread_should_stop()) {
 
                spin_lock_bh(&p->fcoe_rx_list.lock);
-               while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
+               skb_queue_splice_init(&p->fcoe_rx_list, &tmp);
+               spin_unlock_bh(&p->fcoe_rx_list.lock);
+
+               while ((skb = __skb_dequeue(&tmp)) != NULL)
+                       fcoe_recv_frame(skb);
+
+               spin_lock_bh(&p->fcoe_rx_list.lock);
+               if (!skb_queue_len(&p->fcoe_rx_list)) {
                        set_current_state(TASK_INTERRUPTIBLE);
                        spin_unlock_bh(&p->fcoe_rx_list.lock);
                        schedule();
                        set_current_state(TASK_RUNNING);
-                       if (kthread_should_stop())
-                               return 0;
-                       spin_lock_bh(&p->fcoe_rx_list.lock);
-               }
-               spin_unlock_bh(&p->fcoe_rx_list.lock);
-               fcoe_recv_frame(skb);
+               } else
+                       spin_unlock_bh(&p->fcoe_rx_list.lock);
        }
        return 0;
 }
@@ -2187,8 +2186,12 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
        /* start FIP Discovery and FLOGI */
        lport->boot_time = jiffies;
        fc_fabric_login(lport);
-       if (!fcoe_link_ok(lport))
+       if (!fcoe_link_ok(lport)) {
+               rtnl_unlock();
                fcoe_ctlr_link_up(&fcoe->ctlr);
+               mutex_unlock(&fcoe_config_mutex);
+               return rc;
+       }
 
 out_nodev:
        rtnl_unlock();
@@ -2261,31 +2264,14 @@ static int fcoe_link_ok(struct fc_lport *lport)
 static void fcoe_percpu_clean(struct fc_lport *lport)
 {
        struct fcoe_percpu_s *pp;
-       struct fcoe_rcv_info *fr;
-       struct sk_buff_head *list;
-       struct sk_buff *skb, *next;
-       struct sk_buff *head;
+       struct sk_buff *skb;
        unsigned int cpu;
 
        for_each_possible_cpu(cpu) {
                pp = &per_cpu(fcoe_percpu, cpu);
-               spin_lock_bh(&pp->fcoe_rx_list.lock);
-               list = &pp->fcoe_rx_list;
-               head = list->next;
-               for (skb = head; skb != (struct sk_buff *)list;
-                    skb = next) {
-                       next = skb->next;
-                       fr = fcoe_dev_from_skb(skb);
-                       if (fr->fr_dev == lport) {
-                               __skb_unlink(skb, list);
-                               kfree_skb(skb);
-                       }
-               }
 
-               if (!pp->thread || !cpu_online(cpu)) {
-                       spin_unlock_bh(&pp->fcoe_rx_list.lock);
+               if (!pp->thread || !cpu_online(cpu))
                        continue;
-               }
 
                skb = dev_alloc_skb(0);
                if (!skb) {
@@ -2294,6 +2280,7 @@ static void fcoe_percpu_clean(struct fc_lport *lport)
                }
                skb->destructor = fcoe_percpu_flush_done;
 
+               spin_lock_bh(&pp->fcoe_rx_list.lock);
                __skb_queue_tail(&pp->fcoe_rx_list, skb);
                if (pp->fcoe_rx_list.qlen == 1)
                        wake_up_process(pp->thread);
index e7522dcc296eb8bb9c425da842f2a159d05862f2..249a106888d9309ab44ff0f4d2cc82d1b0ff2dc8 100644 (file)
@@ -242,7 +242,7 @@ static void fcoe_ctlr_announce(struct fcoe_ctlr *fip)
                printk(KERN_INFO "libfcoe: host%d: FIP selected "
                       "Fibre-Channel Forwarder MAC %pM\n",
                       fip->lp->host->host_no, sel->fcf_mac);
-               memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
+               memcpy(fip->dest_addr, sel->fcoe_mac, ETH_ALEN);
                fip->map_dest = 0;
        }
 unlock:
@@ -824,6 +824,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
                        memcpy(fcf->fcf_mac,
                               ((struct fip_mac_desc *)desc)->fd_mac,
                               ETH_ALEN);
+                       memcpy(fcf->fcoe_mac, fcf->fcf_mac, ETH_ALEN);
                        if (!is_valid_ether_addr(fcf->fcf_mac)) {
                                LIBFCOE_FIP_DBG(fip,
                                        "Invalid MAC addr %pM in FIP adv\n",
@@ -1013,6 +1014,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
        struct fip_desc *desc;
        struct fip_encaps *els;
        struct fcoe_dev_stats *stats;
+       struct fcoe_fcf *sel;
        enum fip_desc_type els_dtype = 0;
        u8 els_op;
        u8 sub;
@@ -1040,7 +1042,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
                        goto drop;
                /* Drop ELS if there are duplicate critical descriptors */
                if (desc->fip_dtype < 32) {
-                       if (desc_mask & 1U << desc->fip_dtype) {
+                       if ((desc->fip_dtype != FIP_DT_MAC) &&
+                           (desc_mask & 1U << desc->fip_dtype)) {
                                LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
                                                "Descriptors in FIP ELS\n");
                                goto drop;
@@ -1049,17 +1052,32 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
                }
                switch (desc->fip_dtype) {
                case FIP_DT_MAC:
+                       sel = fip->sel_fcf;
                        if (desc_cnt == 1) {
                                LIBFCOE_FIP_DBG(fip, "FIP descriptors "
                                                "received out of order\n");
                                goto drop;
                        }
+                       /*
+                        * Some switch implementations send two MAC descriptors,
+                        * with first MAC(granted_mac) being the FPMA, and the
+                        * second one(fcoe_mac) is used as destination address
+                        * for sending/receiving FCoE packets. FIP traffic is
+                        * sent using fip_mac. For regular switches, both
+                        * fip_mac and fcoe_mac would be the same.
+                        */
+                       if (desc_cnt == 2)
+                               memcpy(granted_mac,
+                                      ((struct fip_mac_desc *)desc)->fd_mac,
+                                      ETH_ALEN);
 
                        if (dlen != sizeof(struct fip_mac_desc))
                                goto len_err;
-                       memcpy(granted_mac,
-                              ((struct fip_mac_desc *)desc)->fd_mac,
-                              ETH_ALEN);
+
+                       if ((desc_cnt == 3) && (sel))
+                               memcpy(sel->fcoe_mac,
+                                      ((struct fip_mac_desc *)desc)->fd_mac,
+                                      ETH_ALEN);
                        break;
                case FIP_DT_FLOGI:
                case FIP_DT_FDISC:
@@ -1273,11 +1291,6 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
                 * No Vx_Port description. Clear all NPIV ports,
                 * followed by physical port
                 */
-               mutex_lock(&lport->lp_mutex);
-               list_for_each_entry(vn_port, &lport->vports, list)
-                       fc_lport_reset(vn_port);
-               mutex_unlock(&lport->lp_mutex);
-
                mutex_lock(&fip->ctlr_mutex);
                per_cpu_ptr(lport->dev_stats,
                            get_cpu())->VLinkFailureCount++;
@@ -1285,6 +1298,11 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
                fcoe_ctlr_reset(fip);
                mutex_unlock(&fip->ctlr_mutex);
 
+               mutex_lock(&lport->lp_mutex);
+               list_for_each_entry(vn_port, &lport->vports, list)
+                       fc_lport_reset(vn_port);
+               mutex_unlock(&lport->lp_mutex);
+
                fc_lport_reset(fip->lp);
                fcoe_ctlr_solicit(fip, NULL);
        } else {
index cdfe5a16de2aefb3cf9a1a42d035bb0b958893e6..e002cd466e9a916d24d6064012d0488c854cd63e 100644 (file)
@@ -104,7 +104,9 @@ static DEFINE_SPINLOCK(ipr_driver_lock);
 static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
        { /* Gemstone, Citrine, Obsidian, and Obsidian-E */
                .mailbox = 0x0042C,
+               .max_cmds = 100,
                .cache_line_size = 0x20,
+               .clear_isr = 1,
                {
                        .set_interrupt_mask_reg = 0x0022C,
                        .clr_interrupt_mask_reg = 0x00230,
@@ -126,7 +128,9 @@ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
        },
        { /* Snipe and Scamp */
                .mailbox = 0x0052C,
+               .max_cmds = 100,
                .cache_line_size = 0x20,
+               .clear_isr = 1,
                {
                        .set_interrupt_mask_reg = 0x00288,
                        .clr_interrupt_mask_reg = 0x0028C,
@@ -148,7 +152,9 @@ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
        },
        { /* CRoC */
                .mailbox = 0x00044,
+               .max_cmds = 1000,
                .cache_line_size = 0x20,
+               .clear_isr = 0,
                {
                        .set_interrupt_mask_reg = 0x00010,
                        .clr_interrupt_mask_reg = 0x00018,
@@ -847,8 +853,6 @@ static void ipr_do_req(struct ipr_cmnd *ipr_cmd,
 
        ipr_trc_hook(ipr_cmd, IPR_TRACE_START, 0);
 
-       mb();
-
        ipr_send_command(ipr_cmd);
 }
 
@@ -982,8 +986,6 @@ static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
 
                ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_IOA_RES_ADDR);
 
-               mb();
-
                ipr_send_command(ipr_cmd);
        } else {
                list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
@@ -4339,8 +4341,7 @@ static struct ipr_resource_entry *ipr_find_starget(struct scsi_target *starget)
 
        list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
                if ((res->bus == starget->channel) &&
-                   (res->target == starget->id) &&
-                   (res->lun == 0)) {
+                   (res->target == starget->id)) {
                        return res;
                }
        }
@@ -4414,12 +4415,14 @@ static void ipr_target_destroy(struct scsi_target *starget)
        struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
 
        if (ioa_cfg->sis64) {
-               if (starget->channel == IPR_ARRAY_VIRTUAL_BUS)
-                       clear_bit(starget->id, ioa_cfg->array_ids);
-               else if (starget->channel == IPR_VSET_VIRTUAL_BUS)
-                       clear_bit(starget->id, ioa_cfg->vset_ids);
-               else if (starget->channel == 0)
-                       clear_bit(starget->id, ioa_cfg->target_ids);
+               if (!ipr_find_starget(starget)) {
+                       if (starget->channel == IPR_ARRAY_VIRTUAL_BUS)
+                               clear_bit(starget->id, ioa_cfg->array_ids);
+                       else if (starget->channel == IPR_VSET_VIRTUAL_BUS)
+                               clear_bit(starget->id, ioa_cfg->vset_ids);
+                       else if (starget->channel == 0)
+                               clear_bit(starget->id, ioa_cfg->target_ids);
+               }
        }
 
        if (sata_port) {
@@ -5048,12 +5051,14 @@ static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg,
                del_timer(&ioa_cfg->reset_cmd->timer);
                ipr_reset_ioa_job(ioa_cfg->reset_cmd);
        } else if ((int_reg & IPR_PCII_HRRQ_UPDATED) == int_reg) {
-               if (ipr_debug && printk_ratelimit())
-                       dev_err(&ioa_cfg->pdev->dev,
-                               "Spurious interrupt detected. 0x%08X\n", int_reg);
-               writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
-               int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
-               return IRQ_NONE;
+               if (ioa_cfg->clear_isr) {
+                       if (ipr_debug && printk_ratelimit())
+                               dev_err(&ioa_cfg->pdev->dev,
+                                       "Spurious interrupt detected. 0x%08X\n", int_reg);
+                       writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
+                       int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
+                       return IRQ_NONE;
+               }
        } else {
                if (int_reg & IPR_PCII_IOA_UNIT_CHECKED)
                        ioa_cfg->ioa_unit_checked = 1;
@@ -5153,6 +5158,9 @@ static irqreturn_t ipr_isr(int irq, void *devp)
                        }
                }
 
+               if (ipr_cmd && !ioa_cfg->clear_isr)
+                       break;
+
                if (ipr_cmd != NULL) {
                        /* Clear the PCI interrupt */
                        num_hrrq = 0;
@@ -5854,14 +5862,12 @@ static int ipr_queuecommand_lck(struct scsi_cmnd *scsi_cmd,
                        rc = ipr_build_ioadl(ioa_cfg, ipr_cmd);
        }
 
-       if (likely(rc == 0)) {
-               mb();
-               ipr_send_command(ipr_cmd);
-       } else {
-                list_move_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
-                return SCSI_MLQUEUE_HOST_BUSY;
+       if (unlikely(rc != 0)) {
+               list_move_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+               return SCSI_MLQUEUE_HOST_BUSY;
        }
 
+       ipr_send_command(ipr_cmd);
        return 0;
 }
 
@@ -6239,8 +6245,6 @@ static unsigned int ipr_qc_issue(struct ata_queued_cmd *qc)
                return AC_ERR_INVALID;
        }
 
-       mb();
-
        ipr_send_command(ipr_cmd);
 
        return 0;
@@ -8277,6 +8281,10 @@ static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
        if (ioa_cfg->ipr_cmd_pool)
                pci_pool_destroy (ioa_cfg->ipr_cmd_pool);
 
+       kfree(ioa_cfg->ipr_cmnd_list);
+       kfree(ioa_cfg->ipr_cmnd_list_dma);
+       ioa_cfg->ipr_cmnd_list = NULL;
+       ioa_cfg->ipr_cmnd_list_dma = NULL;
        ioa_cfg->ipr_cmd_pool = NULL;
 }
 
@@ -8352,11 +8360,19 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
        int i;
 
        ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev,
-                                                sizeof(struct ipr_cmnd), 16, 0);
+                                                sizeof(struct ipr_cmnd), 512, 0);
 
        if (!ioa_cfg->ipr_cmd_pool)
                return -ENOMEM;
 
+       ioa_cfg->ipr_cmnd_list = kcalloc(IPR_NUM_CMD_BLKS, sizeof(struct ipr_cmnd *), GFP_KERNEL);
+       ioa_cfg->ipr_cmnd_list_dma = kcalloc(IPR_NUM_CMD_BLKS, sizeof(dma_addr_t), GFP_KERNEL);
+
+       if (!ioa_cfg->ipr_cmnd_list || !ioa_cfg->ipr_cmnd_list_dma) {
+               ipr_free_cmd_blks(ioa_cfg);
+               return -ENOMEM;
+       }
+
        for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
                ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
 
@@ -8584,6 +8600,7 @@ static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
        host->max_channel = IPR_MAX_BUS_TO_SCAN;
        host->unique_id = host->host_no;
        host->max_cmd_len = IPR_MAX_CDB_LEN;
+       host->can_queue = ioa_cfg->max_cmds;
        pci_set_drvdata(pdev, ioa_cfg);
 
        p = &ioa_cfg->chip_cfg->regs;
@@ -8768,6 +8785,8 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
        /* set SIS 32 or SIS 64 */
        ioa_cfg->sis64 = ioa_cfg->ipr_chip->sis_type == IPR_SIS64 ? 1 : 0;
        ioa_cfg->chip_cfg = ioa_cfg->ipr_chip->cfg;
+       ioa_cfg->clear_isr = ioa_cfg->chip_cfg->clear_isr;
+       ioa_cfg->max_cmds = ioa_cfg->chip_cfg->max_cmds;
 
        if (ipr_transop_timeout)
                ioa_cfg->transop_timeout = ipr_transop_timeout;
index f94eaee2ff16b626ae516a718fced5d76979cd95..153b8bd91d1ef825952ec70fec861fea0e8d5fea 100644 (file)
@@ -38,8 +38,8 @@
 /*
  * Literals
  */
-#define IPR_DRIVER_VERSION "2.5.2"
-#define IPR_DRIVER_DATE "(April 27, 2011)"
+#define IPR_DRIVER_VERSION "2.5.3"
+#define IPR_DRIVER_DATE "(March 10, 2012)"
 
 /*
  * IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding
@@ -53,7 +53,7 @@
  * IPR_NUM_BASE_CMD_BLKS: This defines the maximum number of
  *     ops the mid-layer can send to the adapter.
  */
-#define IPR_NUM_BASE_CMD_BLKS                          100
+#define IPR_NUM_BASE_CMD_BLKS                  (ioa_cfg->max_cmds)
 
 #define PCI_DEVICE_ID_IBM_OBSIDIAN_E   0x0339
 
 #define IPR_NUM_INTERNAL_CMD_BLKS      (IPR_NUM_HCAMS + \
                                      ((IPR_NUM_RESET_RELOAD_RETRIES + 1) * 2) + 4)
 
-#define IPR_MAX_COMMANDS               IPR_NUM_BASE_CMD_BLKS
+#define IPR_MAX_COMMANDS               100
 #define IPR_NUM_CMD_BLKS               (IPR_NUM_BASE_CMD_BLKS + \
                                                IPR_NUM_INTERNAL_CMD_BLKS)
 
@@ -1305,7 +1305,9 @@ struct ipr_interrupts {
 
 struct ipr_chip_cfg_t {
        u32 mailbox;
+       u16 max_cmds;
        u8 cache_line_size;
+       u8 clear_isr;
        struct ipr_interrupt_offsets regs;
 };
 
@@ -1388,6 +1390,7 @@ struct ipr_ioa_cfg {
        u8 sis64:1;
        u8 dump_timeout:1;
        u8 cfg_locked:1;
+       u8 clear_isr:1;
 
        u8 revid;
 
@@ -1501,8 +1504,9 @@ struct ipr_ioa_cfg {
        struct ata_host ata_host;
        char ipr_cmd_label[8];
 #define IPR_CMD_LABEL          "ipr_cmd"
-       struct ipr_cmnd *ipr_cmnd_list[IPR_NUM_CMD_BLKS];
-       dma_addr_t ipr_cmnd_list_dma[IPR_NUM_CMD_BLKS];
+       u32 max_cmds;
+       struct ipr_cmnd **ipr_cmnd_list;
+       dma_addr_t *ipr_cmnd_list_dma;
 }; /* struct ipr_ioa_cfg */
 
 struct ipr_cmnd {
index 630291f018262a3143645afe911ea97be1365f55..aceffadb21c79d347fdc7f138bce81afaf2394c9 100644 (file)
@@ -2263,7 +2263,18 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
        mp->class = class;
        /* adjust em exch xid range for offload */
        mp->min_xid = min_xid;
-       mp->max_xid = max_xid;
+
+       /* reduce range so per cpu pool fits into PCPU_MIN_UNIT_SIZE pool */
+       pool_exch_range = (PCPU_MIN_UNIT_SIZE - sizeof(*pool)) /
+               sizeof(struct fc_exch *);
+       if ((max_xid - min_xid + 1) / (fc_cpu_mask + 1) > pool_exch_range) {
+               mp->max_xid = pool_exch_range * (fc_cpu_mask + 1) +
+                       min_xid - 1;
+       } else {
+               mp->max_xid = max_xid;
+               pool_exch_range = (mp->max_xid - mp->min_xid + 1) /
+                       (fc_cpu_mask + 1);
+       }
 
        mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep);
        if (!mp->ep_pool)
@@ -2274,7 +2285,6 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
         * divided across all cpus. The exch pointers array memory is
         * allocated for exch range per pool.
         */
-       pool_exch_range = (mp->max_xid - mp->min_xid + 1) / (fc_cpu_mask + 1);
        mp->pool_max_index = pool_exch_range - 1;
 
        /*
index bd5d31d022d911f245f87322ea84dbe01f7b2c93..ef9560dff295f9252bf9ccdf3a4d9d2de61d1852 100644 (file)
@@ -1743,8 +1743,16 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
        mfs = ntohs(flp->fl_csp.sp_bb_data) &
                FC_SP_BB_DATA_MASK;
        if (mfs >= FC_SP_MIN_MAX_PAYLOAD &&
-           mfs < lport->mfs)
+           mfs <= lport->mfs) {
                lport->mfs = mfs;
+               fc_host_maxframe_size(lport->host) = mfs;
+       } else {
+               FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, "
+                            "lport->mfs:%hu\n", mfs, lport->mfs);
+               fc_lport_error(lport, fp);
+               goto err;
+       }
+
        csp_flags = ntohs(flp->fl_csp.sp_features);
        r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov);
        e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
index 88928f00aa2db3dc1896f93f1b9c261be77e629d..fe5d396aca73b7b6bc13e0d8fd3199f9759b7a82 100644 (file)
@@ -1,7 +1,7 @@
 #/*******************************************************************
 # * This file is part of the Emulex Linux Device Driver for         *
 # * Fibre Channel Host Bus Adapters.                                *
-# * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
+# * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
 # * EMULEX and SLI are trademarks of Emulex.                        *
 # * www.emulex.com                                                  *
 # *                                                                 *
@@ -22,6 +22,8 @@
 ccflags-$(GCOV) := -fprofile-arcs -ftest-coverage
 ccflags-$(GCOV) += -O0
 
+ccflags-y += -Werror
+
 obj-$(CONFIG_SCSI_LPFC) := lpfc.o
 
 lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o lpfc_hbadisc.o \
index 5fc044ff656eb75f85bf61ec2666aa2465c35f02..3a1ffdd6d831ebfdd80fdb87b5b3dc15b0444cff 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -840,6 +840,8 @@ struct lpfc_hba {
        struct dentry *debug_dumpData;   /* BlockGuard BPL */
        struct dentry *debug_dumpDif;    /* BlockGuard BPL */
        struct dentry *debug_InjErrLBA;  /* LBA to inject errors at */
+       struct dentry *debug_InjErrNPortID;  /* NPortID to inject errors at */
+       struct dentry *debug_InjErrWWPN;  /* WWPN to inject errors at */
        struct dentry *debug_writeGuard; /* inject write guard_tag errors */
        struct dentry *debug_writeApp;   /* inject write app_tag errors */
        struct dentry *debug_writeRef;   /* inject write ref_tag errors */
@@ -854,6 +856,8 @@ struct lpfc_hba {
        uint32_t lpfc_injerr_rgrd_cnt;
        uint32_t lpfc_injerr_rapp_cnt;
        uint32_t lpfc_injerr_rref_cnt;
+       uint32_t lpfc_injerr_nportid;
+       struct lpfc_name lpfc_injerr_wwpn;
        sector_t lpfc_injerr_lba;
 #define LPFC_INJERR_LBA_OFF    (sector_t)(-1)
 
@@ -908,6 +912,8 @@ struct lpfc_hba {
        atomic_t fast_event_count;
        uint32_t fcoe_eventtag;
        uint32_t fcoe_eventtag_at_fcf_scan;
+       uint32_t fcoe_cvl_eventtag;
+       uint32_t fcoe_cvl_eventtag_attn;
        struct lpfc_fcf fcf;
        uint8_t fc_map[3];
        uint8_t valid_vlan;
index 296ad5bc42400692116686ac764570a350181896..5eb2bc11618368ca9b942b9f0fbcb3eeee103fd6 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -2575,7 +2575,7 @@ LPFC_VPORT_ATTR_HEX_RW(log_verbose, 0x0, 0x0, 0xffffffff,
 # lpfc_enable_da_id: This turns on the DA_ID CT command that deregisters
 # objects that have been registered with the nameserver after login.
 */
-LPFC_VPORT_ATTR_R(enable_da_id, 0, 0, 1,
+LPFC_VPORT_ATTR_R(enable_da_id, 1, 0, 1,
                  "Deregister nameserver objects before LOGO");
 
 /*
index 22e17be04d8af228b33efef2009f2a9cfc7a7471..5bdf2eecb1782dd4c33f863607d045ed27535fe9 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2007-2011 Emulex.  All rights reserved.           *
+ * Copyright (C) 2007-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
@@ -1010,25 +1010,35 @@ lpfc_debugfs_dif_err_read(struct file *file, char __user *buf,
 {
        struct dentry *dent = file->f_dentry;
        struct lpfc_hba *phba = file->private_data;
-       char cbuf[16];
+       char cbuf[32];
+       uint64_t tmp = 0;
        int cnt = 0;
 
        if (dent == phba->debug_writeGuard)
-               cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wgrd_cnt);
+               cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wgrd_cnt);
        else if (dent == phba->debug_writeApp)
-               cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wapp_cnt);
+               cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wapp_cnt);
        else if (dent == phba->debug_writeRef)
-               cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wref_cnt);
+               cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wref_cnt);
        else if (dent == phba->debug_readGuard)
-               cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rgrd_cnt);
+               cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rgrd_cnt);
        else if (dent == phba->debug_readApp)
-               cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rapp_cnt);
+               cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rapp_cnt);
        else if (dent == phba->debug_readRef)
-               cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rref_cnt);
-       else if (dent == phba->debug_InjErrLBA)
-               cnt = snprintf(cbuf, 16, "0x%lx\n",
-                                (unsigned long) phba->lpfc_injerr_lba);
-       else
+               cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rref_cnt);
+       else if (dent == phba->debug_InjErrNPortID)
+               cnt = snprintf(cbuf, 32, "0x%06x\n", phba->lpfc_injerr_nportid);
+       else if (dent == phba->debug_InjErrWWPN) {
+               memcpy(&tmp, &phba->lpfc_injerr_wwpn, sizeof(struct lpfc_name));
+               tmp = cpu_to_be64(tmp);
+               cnt = snprintf(cbuf, 32, "0x%016llx\n", tmp);
+       } else if (dent == phba->debug_InjErrLBA) {
+               if (phba->lpfc_injerr_lba == (sector_t)(-1))
+                       cnt = snprintf(cbuf, 32, "off\n");
+               else
+                       cnt = snprintf(cbuf, 32, "0x%llx\n",
+                                (uint64_t) phba->lpfc_injerr_lba);
+       } else
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                         "0547 Unknown debugfs error injection entry\n");
 
@@ -1042,7 +1052,7 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
        struct dentry *dent = file->f_dentry;
        struct lpfc_hba *phba = file->private_data;
        char dstbuf[32];
-       unsigned long tmp;
+       uint64_t tmp = 0;
        int size;
 
        memset(dstbuf, 0, 32);
@@ -1050,7 +1060,12 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
        if (copy_from_user(dstbuf, buf, size))
                return 0;
 
-       if (strict_strtoul(dstbuf, 0, &tmp))
+       if (dent == phba->debug_InjErrLBA) {
+               if ((buf[0] == 'o') && (buf[1] == 'f') && (buf[2] == 'f'))
+                       tmp = (uint64_t)(-1);
+       }
+
+       if ((tmp == 0) && (kstrtoull(dstbuf, 0, &tmp)))
                return 0;
 
        if (dent == phba->debug_writeGuard)
@@ -1067,7 +1082,12 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
                phba->lpfc_injerr_rref_cnt = (uint32_t)tmp;
        else if (dent == phba->debug_InjErrLBA)
                phba->lpfc_injerr_lba = (sector_t)tmp;
-       else
+       else if (dent == phba->debug_InjErrNPortID)
+               phba->lpfc_injerr_nportid = (uint32_t)(tmp & Mask_DID);
+       else if (dent == phba->debug_InjErrWWPN) {
+               tmp = cpu_to_be64(tmp);
+               memcpy(&phba->lpfc_injerr_wwpn, &tmp, sizeof(struct lpfc_name));
+       } else
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                         "0548 Unknown debugfs error injection entry\n");
 
@@ -3949,6 +3969,28 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
                }
                phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
 
+               snprintf(name, sizeof(name), "InjErrNPortID");
+               phba->debug_InjErrNPortID =
+                       debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+                       phba->hba_debugfs_root,
+                       phba, &lpfc_debugfs_op_dif_err);
+               if (!phba->debug_InjErrNPortID) {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+                               "0809 Cannot create debugfs InjErrNPortID\n");
+                       goto debug_failed;
+               }
+
+               snprintf(name, sizeof(name), "InjErrWWPN");
+               phba->debug_InjErrWWPN =
+                       debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+                       phba->hba_debugfs_root,
+                       phba, &lpfc_debugfs_op_dif_err);
+               if (!phba->debug_InjErrWWPN) {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+                               "0810 Cannot create debugfs InjErrWWPN\n");
+                       goto debug_failed;
+               }
+
                snprintf(name, sizeof(name), "writeGuardInjErr");
                phba->debug_writeGuard =
                        debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
@@ -4321,6 +4363,14 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
                        debugfs_remove(phba->debug_InjErrLBA); /* InjErrLBA */
                        phba->debug_InjErrLBA = NULL;
                }
+               if (phba->debug_InjErrNPortID) {         /* InjErrNPortID */
+                       debugfs_remove(phba->debug_InjErrNPortID);
+                       phba->debug_InjErrNPortID = NULL;
+               }
+               if (phba->debug_InjErrWWPN) {
+                       debugfs_remove(phba->debug_InjErrWWPN); /* InjErrWWPN */
+                       phba->debug_InjErrWWPN = NULL;
+               }
                if (phba->debug_writeGuard) {
                        debugfs_remove(phba->debug_writeGuard); /* writeGuard */
                        phba->debug_writeGuard = NULL;
index 8db2fb3b45ec13a40fd36ad3aa4c48cf9659b37c..3407b39e0a3f82bfaf8239864ea1b764b2b386d6 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -925,9 +925,17 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                 * due to new FCF discovery
                 */
                if ((phba->hba_flag & HBA_FIP_SUPPORT) &&
-                   (phba->fcf.fcf_flag & FCF_DISCOVERY) &&
-                   !((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
-                    (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))) {
+                   (phba->fcf.fcf_flag & FCF_DISCOVERY)) {
+                       if (phba->link_state < LPFC_LINK_UP)
+                               goto stop_rr_fcf_flogi;
+                       if ((phba->fcoe_cvl_eventtag_attn ==
+                            phba->fcoe_cvl_eventtag) &&
+                           (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                           (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))
+                               goto stop_rr_fcf_flogi;
+                       else
+                               phba->fcoe_cvl_eventtag_attn =
+                                       phba->fcoe_cvl_eventtag;
                        lpfc_printf_log(phba, KERN_WARNING, LOG_FIP | LOG_ELS,
                                        "2611 FLOGI failed on FCF (x%x), "
                                        "status:x%x/x%x, tmo:x%x, perform "
@@ -943,6 +951,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                                goto out;
                }
 
+stop_rr_fcf_flogi:
                /* FLOGI failure */
                lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
                                "2858 FLOGI failure Status:x%x/x%x TMO:x%x\n",
index 343d87ba4df8f2e58cb2685c02bd86909d64e793..b507536dc5b569262f33b04df00cab96f48c4ab5 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -2843,7 +2843,14 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
        struct lpfc_vport *vport = mboxq->vport;
        struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
 
-       if (mboxq->u.mb.mbxStatus) {
+       /*
+        * VFI not supported for interface type 0, so ignore any mailbox
+        * error (except VFI in use) and continue with the discovery.
+        */
+       if (mboxq->u.mb.mbxStatus &&
+           (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+                       LPFC_SLI_INTF_IF_TYPE_0) &&
+           mboxq->u.mb.mbxStatus != MBX_VFI_IN_USE) {
                lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
                         "2018 REG_VFI mbxStatus error x%x "
                         "HBA state x%x\n",
@@ -5673,14 +5680,13 @@ lpfc_fcf_inuse(struct lpfc_hba *phba)
                                ret = 1;
                                spin_unlock_irq(shost->host_lock);
                                goto out;
-                       } else {
+                       } else if (ndlp->nlp_flag & NLP_RPI_REGISTERED) {
+                               ret = 1;
                                lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
-                                       "2624 RPI %x DID %x flg %x still "
-                                       "logged in\n",
-                                       ndlp->nlp_rpi, ndlp->nlp_DID,
-                                       ndlp->nlp_flag);
-                               if (ndlp->nlp_flag & NLP_RPI_REGISTERED)
-                                       ret = 1;
+                                               "2624 RPI %x DID %x flag %x "
+                                               "still logged in\n",
+                                               ndlp->nlp_rpi, ndlp->nlp_DID,
+                                               ndlp->nlp_flag);
                        }
                }
                spin_unlock_irq(shost->host_lock);
index 9e2b9b227e1a2b732b39ee6cb424d421c6c03230..91f09761bd328136142fd3a37bd737789c6cee47 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2009 Emulex.  All rights reserved.                *
+ * Copyright (C) 2009-2012 Emulex.  All rights reserved.                *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
@@ -338,6 +338,12 @@ struct lpfc_cqe {
 #define CQE_CODE_XRI_ABORTED           0x5
 #define CQE_CODE_RECEIVE_V1            0x9
 
+/*
+ * Define mask value for xri_aborted and wcqe completed CQE extended status.
+ * Currently, extended status is limited to 9 bits (0x0 -> 0x103) .
+ */
+#define WCQE_PARAM_MASK                0x1FF;
+
 /* completion queue entry for wqe completions */
 struct lpfc_wcqe_complete {
        uint32_t word0;
index b38f99f3be32b9e6e8bff87809b52d704e766070..9598fdcb08ab0ee50b147574d05406eee286376e 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -2704,16 +2704,14 @@ lpfc_offline_prep(struct lpfc_hba * phba)
                                }
                                spin_lock_irq(shost->host_lock);
                                ndlp->nlp_flag &= ~NLP_NPR_ADISC;
-
+                               spin_unlock_irq(shost->host_lock);
                                /*
                                 * Whenever an SLI4 port goes offline, free the
-                                * RPI.  A new RPI when the adapter port comes
-                                * back online.
+                                * RPI. Get a new RPI when the adapter port
+                                * comes back online.
                                 */
                                if (phba->sli_rev == LPFC_SLI_REV4)
                                        lpfc_sli4_free_rpi(phba, ndlp->nlp_rpi);
-
-                               spin_unlock_irq(shost->host_lock);
                                lpfc_unreg_rpi(vports[i], ndlp);
                        }
                }
@@ -2786,9 +2784,13 @@ lpfc_scsi_buf_update(struct lpfc_hba *phba)
 
        spin_lock_irq(&phba->hbalock);
        spin_lock(&phba->scsi_buf_list_lock);
-       list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list)
+       list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list) {
                sb->cur_iocbq.sli4_xritag =
                        phba->sli4_hba.xri_ids[sb->cur_iocbq.sli4_lxritag];
+               set_bit(sb->cur_iocbq.sli4_lxritag, phba->sli4_hba.xri_bmask);
+               phba->sli4_hba.max_cfg_param.xri_used++;
+               phba->sli4_hba.xri_count++;
+       }
        spin_unlock(&phba->scsi_buf_list_lock);
        spin_unlock_irq(&phba->hbalock);
        return 0;
@@ -3723,6 +3725,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
                break;
 
        case LPFC_FIP_EVENT_TYPE_FCF_DEAD:
+               phba->fcoe_cvl_eventtag = acqe_fip->event_tag;
                lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY,
                        "2549 FCF (x%x) disconnected from network, "
                        "tag:x%x\n", acqe_fip->index, acqe_fip->event_tag);
@@ -3784,6 +3787,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
                }
                break;
        case LPFC_FIP_EVENT_TYPE_CVL:
+               phba->fcoe_cvl_eventtag = acqe_fip->event_tag;
                lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY,
                        "2718 Clear Virtual Link Received for VPI 0x%x"
                        " tag 0x%x\n", acqe_fip->index, acqe_fip->event_tag);
@@ -5226,8 +5230,7 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
         * rpi is normalized to a zero base because the physical rpi is
         * port based.
         */
-       curr_rpi_range = phba->sli4_hba.next_rpi -
-               phba->sli4_hba.max_cfg_param.rpi_base;
+       curr_rpi_range = phba->sli4_hba.next_rpi;
        spin_unlock_irq(&phba->hbalock);
 
        /*
@@ -5818,10 +5821,9 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
                                        readl(phba->sli4_hba.u.if_type2.
                                              ERR2regaddr);
                                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                                       "2888 Port Error Detected "
-                                       "during POST: "
-                                       "port status reg 0x%x, "
-                                       "port_smphr reg 0x%x, "
+                                       "2888 Unrecoverable port error "
+                                       "following POST: port status reg "
+                                       "0x%x, port_smphr reg 0x%x, "
                                        "error 1=0x%x, error 2=0x%x\n",
                                        reg_data.word0,
                                        portsmphr_reg.word0,
@@ -6142,7 +6144,6 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
                phba->sli4_hba.next_xri = phba->sli4_hba.max_cfg_param.xri_base;
                phba->vpi_base = phba->sli4_hba.max_cfg_param.vpi_base;
                phba->vfi_base = phba->sli4_hba.max_cfg_param.vfi_base;
-               phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base;
                phba->max_vpi = (phba->sli4_hba.max_cfg_param.max_vpi > 0) ?
                                (phba->sli4_hba.max_cfg_param.max_vpi - 1) : 0;
                phba->max_vports = phba->max_vpi;
@@ -7231,6 +7232,7 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
        uint32_t rdy_chk, num_resets = 0, reset_again = 0;
        union lpfc_sli4_cfg_shdr *shdr;
        struct lpfc_register reg_data;
+       uint16_t devid;
 
        if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
        switch (if_type) {
@@ -7277,7 +7279,9 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
                               LPFC_SLIPORT_INIT_PORT);
                        writel(reg_data.word0, phba->sli4_hba.u.if_type2.
                               CTRLregaddr);
-
+                       /* flush */
+                       pci_read_config_word(phba->pcidev,
+                                            PCI_DEVICE_ID, &devid);
                        /*
                         * Poll the Port Status Register and wait for RDY for
                         * up to 10 seconds.  If the port doesn't respond, treat
@@ -7315,11 +7319,10 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
                                phba->work_status[1] = readl(
                                        phba->sli4_hba.u.if_type2.ERR2regaddr);
                                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                                       "2890 Port Error Detected "
-                                       "during Port Reset: "
-                                       "port status reg 0x%x, "
+                                       "2890 Port error detected during port "
+                                       "reset(%d): port status reg 0x%x, "
                                        "error 1=0x%x, error 2=0x%x\n",
-                                       reg_data.word0,
+                                       num_resets, reg_data.word0,
                                        phba->work_status[0],
                                        phba->work_status[1]);
                                rc = -ENODEV;
index 7b6b2aa5795aba306e54830c0224a5fd3c8048be..15ca2a9a0cdd3122850fac7fbd3ca2991949a31b 100644 (file)
@@ -1,7 +1,7 @@
  /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -440,11 +440,15 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                spin_unlock_irq(shost->host_lock);
                stat.un.b.lsRjtRsnCode = LSRJT_INVALID_CMD;
                stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
-               lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
+               rc = lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
                        ndlp, mbox);
+               if (rc)
+                       mempool_free(mbox, phba->mbox_mem_pool);
                return 1;
        }
-       lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox);
+       rc = lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox);
+       if (rc)
+               mempool_free(mbox, phba->mbox_mem_pool);
        return 1;
 out:
        stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
index efc055b6bac4fe6fffb8432c4004ea2cbc9ac5aa..88f3a83dbd2eaf45a36d08a0c92b8fe186158ea3 100644 (file)
@@ -39,8 +39,8 @@
 #include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
-#include "lpfc_scsi.h"
 #include "lpfc.h"
+#include "lpfc_scsi.h"
 #include "lpfc_logmsg.h"
 #include "lpfc_crtn.h"
 #include "lpfc_vport.h"
 int _dump_buf_done;
 
 static char *dif_op_str[] = {
-       "SCSI_PROT_NORMAL",
-       "SCSI_PROT_READ_INSERT",
-       "SCSI_PROT_WRITE_STRIP",
-       "SCSI_PROT_READ_STRIP",
-       "SCSI_PROT_WRITE_INSERT",
-       "SCSI_PROT_READ_PASS",
-       "SCSI_PROT_WRITE_PASS",
+       "PROT_NORMAL",
+       "PROT_READ_INSERT",
+       "PROT_WRITE_STRIP",
+       "PROT_READ_STRIP",
+       "PROT_WRITE_INSERT",
+       "PROT_READ_PASS",
+       "PROT_WRITE_PASS",
+};
+
+static char *dif_grd_str[] = {
+       "NO_GUARD",
+       "DIF_CRC",
+       "DIX_IP",
 };
 
 struct scsi_dif_tuple {
@@ -1281,10 +1287,14 @@ lpfc_cmd_blksize(struct scsi_cmnd *sc)
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
 
-#define BG_ERR_INIT    1
-#define BG_ERR_TGT     2
-#define BG_ERR_SWAP    3
-#define BG_ERR_CHECK   4
+/* Return if if error injection is detected by Initiator */
+#define BG_ERR_INIT    0x1
+/* Return if if error injection is detected by Target */
+#define BG_ERR_TGT     0x2
+/* Return if if swapping CSUM<-->CRC is required for error injection */
+#define BG_ERR_SWAP    0x10
+/* Return if disabling Guard/Ref/App checking is required for error injection */
+#define BG_ERR_CHECK   0x20
 
 /**
  * lpfc_bg_err_inject - Determine if we should inject an error
@@ -1294,10 +1304,7 @@ lpfc_cmd_blksize(struct scsi_cmnd *sc)
  * @apptag: (out) BlockGuard application tag for transmitted data
  * @new_guard (in) Value to replace CRC with if needed
  *
- * Returns (1) if error injection is detected by Initiator
- * Returns (2) if error injection is detected by Target
- * Returns (3) if swapping CSUM->CRC is required for error injection
- * Returns (4) disabling Guard/Ref/App checking is required for error injection
+ * Returns BG_ERR_* bit mask or 0 if request ignored
  **/
 static int
 lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
@@ -1305,7 +1312,10 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
 {
        struct scatterlist *sgpe; /* s/g prot entry */
        struct scatterlist *sgde; /* s/g data entry */
+       struct lpfc_scsi_buf *lpfc_cmd = NULL;
        struct scsi_dif_tuple *src = NULL;
+       struct lpfc_nodelist *ndlp;
+       struct lpfc_rport_data *rdata;
        uint32_t op = scsi_get_prot_op(sc);
        uint32_t blksize;
        uint32_t numblks;
@@ -1318,8 +1328,9 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
 
        sgpe = scsi_prot_sglist(sc);
        sgde = scsi_sglist(sc);
-
        lba = scsi_get_lba(sc);
+
+       /* First check if we need to match the LBA */
        if (phba->lpfc_injerr_lba != LPFC_INJERR_LBA_OFF) {
                blksize = lpfc_cmd_blksize(sc);
                numblks = (scsi_bufflen(sc) + blksize - 1) / blksize;
@@ -1334,66 +1345,123 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                                sizeof(struct scsi_dif_tuple);
                        if (numblks < blockoff)
                                blockoff = numblks;
-                       src = (struct scsi_dif_tuple *)sg_virt(sgpe);
-                       src += blockoff;
                }
        }
 
+       /* Next check if we need to match the remote NPortID or WWPN */
+       rdata = sc->device->hostdata;
+       if (rdata && rdata->pnode) {
+               ndlp = rdata->pnode;
+
+               /* Make sure we have the right NPortID if one is specified */
+               if (phba->lpfc_injerr_nportid  &&
+                       (phba->lpfc_injerr_nportid != ndlp->nlp_DID))
+                       return 0;
+
+               /*
+                * Make sure we have the right WWPN if one is specified.
+                * wwn[0] should be a non-zero NAA in a good WWPN.
+                */
+               if (phba->lpfc_injerr_wwpn.u.wwn[0]  &&
+                       (memcmp(&ndlp->nlp_portname, &phba->lpfc_injerr_wwpn,
+                               sizeof(struct lpfc_name)) != 0))
+                       return 0;
+       }
+
+       /* Setup a ptr to the protection data if the SCSI host provides it */
+       if (sgpe) {
+               src = (struct scsi_dif_tuple *)sg_virt(sgpe);
+               src += blockoff;
+               lpfc_cmd = (struct lpfc_scsi_buf *)sc->host_scribble;
+       }
+
        /* Should we change the Reference Tag */
        if (reftag) {
                if (phba->lpfc_injerr_wref_cnt) {
                        switch (op) {
                        case SCSI_PROT_WRITE_PASS:
-                               if (blockoff && src) {
-                                       /* Insert error in middle of the IO */
+                               if (src) {
+                                       /*
+                                        * For WRITE_PASS, force the error
+                                        * to be sent on the wire. It should
+                                        * be detected by the Target.
+                                        * If blockoff != 0 error will be
+                                        * inserted in middle of the IO.
+                                        */
 
                                        lpfc_printf_log(phba, KERN_ERR, LOG_BG,
                                        "9076 BLKGRD: Injecting reftag error: "
                                        "write lba x%lx + x%x oldrefTag x%x\n",
                                        (unsigned long)lba, blockoff,
-                                       src->ref_tag);
+                                       be32_to_cpu(src->ref_tag));
 
                                        /*
-                                        * NOTE, this will change ref tag in
-                                        * the memory location forever!
+                                        * Save the old ref_tag so we can
+                                        * restore it on completion.
                                         */
-                                       src->ref_tag = 0xDEADBEEF;
+                                       if (lpfc_cmd) {
+                                               lpfc_cmd->prot_data_type =
+                                                       LPFC_INJERR_REFTAG;
+                                               lpfc_cmd->prot_data_segment =
+                                                       src;
+                                               lpfc_cmd->prot_data =
+                                                       src->ref_tag;
+                                       }
+                                       src->ref_tag = cpu_to_be32(0xDEADBEEF);
                                        phba->lpfc_injerr_wref_cnt--;
-                                       phba->lpfc_injerr_lba =
-                                               LPFC_INJERR_LBA_OFF;
-                                       rc = BG_ERR_CHECK;
+                                       if (phba->lpfc_injerr_wref_cnt == 0) {
+                                               phba->lpfc_injerr_nportid = 0;
+                                               phba->lpfc_injerr_lba =
+                                                       LPFC_INJERR_LBA_OFF;
+                                               memset(&phba->lpfc_injerr_wwpn,
+                                                 0, sizeof(struct lpfc_name));
+                                       }
+                                       rc = BG_ERR_TGT | BG_ERR_CHECK;
+
                                        break;
                                }
                                /* Drop thru */
-                       case SCSI_PROT_WRITE_STRIP:
+                       case SCSI_PROT_WRITE_INSERT:
                                /*
-                                * For WRITE_STRIP and WRITE_PASS,
-                                * force the error on data
-                                * being copied from SLI-Host to SLI-Port.
+                                * For WRITE_INSERT, force the error
+                                * to be sent on the wire. It should be
+                                * detected by the Target.
                                 */
+                               /* DEADBEEF will be the reftag on the wire */
                                *reftag = 0xDEADBEEF;
                                phba->lpfc_injerr_wref_cnt--;
-                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                               rc = BG_ERR_INIT;
+                               if (phba->lpfc_injerr_wref_cnt == 0) {
+                                       phba->lpfc_injerr_nportid = 0;
+                                       phba->lpfc_injerr_lba =
+                                       LPFC_INJERR_LBA_OFF;
+                                       memset(&phba->lpfc_injerr_wwpn,
+                                               0, sizeof(struct lpfc_name));
+                               }
+                               rc = BG_ERR_TGT | BG_ERR_CHECK;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "9077 BLKGRD: Injecting reftag error: "
+                                       "9078 BLKGRD: Injecting reftag error: "
                                        "write lba x%lx\n", (unsigned long)lba);
                                break;
-                       case SCSI_PROT_WRITE_INSERT:
+                       case SCSI_PROT_WRITE_STRIP:
                                /*
-                                * For WRITE_INSERT, force the
-                                * error to be sent on the wire. It should be
-                                * detected by the Target.
+                                * For WRITE_STRIP and WRITE_PASS,
+                                * force the error on data
+                                * being copied from SLI-Host to SLI-Port.
                                 */
-                               /* DEADBEEF will be the reftag on the wire */
                                *reftag = 0xDEADBEEF;
                                phba->lpfc_injerr_wref_cnt--;
-                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                               rc = BG_ERR_TGT;
+                               if (phba->lpfc_injerr_wref_cnt == 0) {
+                                       phba->lpfc_injerr_nportid = 0;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       memset(&phba->lpfc_injerr_wwpn,
+                                               0, sizeof(struct lpfc_name));
+                               }
+                               rc = BG_ERR_INIT;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "9078 BLKGRD: Injecting reftag error: "
+                                       "9077 BLKGRD: Injecting reftag error: "
                                        "write lba x%lx\n", (unsigned long)lba);
                                break;
                        }
@@ -1401,11 +1469,6 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                if (phba->lpfc_injerr_rref_cnt) {
                        switch (op) {
                        case SCSI_PROT_READ_INSERT:
-                               /*
-                                * For READ_INSERT, it doesn't make sense
-                                * to change the reftag.
-                                */
-                               break;
                        case SCSI_PROT_READ_STRIP:
                        case SCSI_PROT_READ_PASS:
                                /*
@@ -1415,7 +1478,13 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                                 */
                                *reftag = 0xDEADBEEF;
                                phba->lpfc_injerr_rref_cnt--;
-                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+                               if (phba->lpfc_injerr_rref_cnt == 0) {
+                                       phba->lpfc_injerr_nportid = 0;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       memset(&phba->lpfc_injerr_wwpn,
+                                               0, sizeof(struct lpfc_name));
+                               }
                                rc = BG_ERR_INIT;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
@@ -1431,56 +1500,87 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                if (phba->lpfc_injerr_wapp_cnt) {
                        switch (op) {
                        case SCSI_PROT_WRITE_PASS:
-                               if (blockoff && src) {
-                                       /* Insert error in middle of the IO */
+                               if (src) {
+                                       /*
+                                        * For WRITE_PASS, force the error
+                                        * to be sent on the wire. It should
+                                        * be detected by the Target.
+                                        * If blockoff != 0 error will be
+                                        * inserted in middle of the IO.
+                                        */
 
                                        lpfc_printf_log(phba, KERN_ERR, LOG_BG,
                                        "9080 BLKGRD: Injecting apptag error: "
                                        "write lba x%lx + x%x oldappTag x%x\n",
                                        (unsigned long)lba, blockoff,
-                                       src->app_tag);
+                                       be16_to_cpu(src->app_tag));
 
                                        /*
-                                        * NOTE, this will change app tag in
-                                        * the memory location forever!
+                                        * Save the old app_tag so we can
+                                        * restore it on completion.
                                         */
-                                       src->app_tag = 0xDEAD;
+                                       if (lpfc_cmd) {
+                                               lpfc_cmd->prot_data_type =
+                                                       LPFC_INJERR_APPTAG;
+                                               lpfc_cmd->prot_data_segment =
+                                                       src;
+                                               lpfc_cmd->prot_data =
+                                                       src->app_tag;
+                                       }
+                                       src->app_tag = cpu_to_be16(0xDEAD);
                                        phba->lpfc_injerr_wapp_cnt--;
-                                       phba->lpfc_injerr_lba =
-                                               LPFC_INJERR_LBA_OFF;
-                                       rc = BG_ERR_CHECK;
+                                       if (phba->lpfc_injerr_wapp_cnt == 0) {
+                                               phba->lpfc_injerr_nportid = 0;
+                                               phba->lpfc_injerr_lba =
+                                                       LPFC_INJERR_LBA_OFF;
+                                               memset(&phba->lpfc_injerr_wwpn,
+                                                 0, sizeof(struct lpfc_name));
+                                       }
+                                       rc = BG_ERR_TGT | BG_ERR_CHECK;
                                        break;
                                }
                                /* Drop thru */
-                       case SCSI_PROT_WRITE_STRIP:
+                       case SCSI_PROT_WRITE_INSERT:
                                /*
-                                * For WRITE_STRIP and WRITE_PASS,
-                                * force the error on data
-                                * being copied from SLI-Host to SLI-Port.
+                                * For WRITE_INSERT, force the
+                                * error to be sent on the wire. It should be
+                                * detected by the Target.
                                 */
+                               /* DEAD will be the apptag on the wire */
                                *apptag = 0xDEAD;
                                phba->lpfc_injerr_wapp_cnt--;
-                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                               rc = BG_ERR_INIT;
+                               if (phba->lpfc_injerr_wapp_cnt == 0) {
+                                       phba->lpfc_injerr_nportid = 0;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       memset(&phba->lpfc_injerr_wwpn,
+                                               0, sizeof(struct lpfc_name));
+                               }
+                               rc = BG_ERR_TGT | BG_ERR_CHECK;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "0812 BLKGRD: Injecting apptag error: "
+                                       "0813 BLKGRD: Injecting apptag error: "
                                        "write lba x%lx\n", (unsigned long)lba);
                                break;
-                       case SCSI_PROT_WRITE_INSERT:
+                       case SCSI_PROT_WRITE_STRIP:
                                /*
-                                * For WRITE_INSERT, force the
-                                * error to be sent on the wire. It should be
-                                * detected by the Target.
+                                * For WRITE_STRIP and WRITE_PASS,
+                                * force the error on data
+                                * being copied from SLI-Host to SLI-Port.
                                 */
-                               /* DEAD will be the apptag on the wire */
                                *apptag = 0xDEAD;
                                phba->lpfc_injerr_wapp_cnt--;
-                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
-                               rc = BG_ERR_TGT;
+                               if (phba->lpfc_injerr_wapp_cnt == 0) {
+                                       phba->lpfc_injerr_nportid = 0;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       memset(&phba->lpfc_injerr_wwpn,
+                                               0, sizeof(struct lpfc_name));
+                               }
+                               rc = BG_ERR_INIT;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "0813 BLKGRD: Injecting apptag error: "
+                                       "0812 BLKGRD: Injecting apptag error: "
                                        "write lba x%lx\n", (unsigned long)lba);
                                break;
                        }
@@ -1488,11 +1588,6 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                if (phba->lpfc_injerr_rapp_cnt) {
                        switch (op) {
                        case SCSI_PROT_READ_INSERT:
-                               /*
-                                * For READ_INSERT, it doesn't make sense
-                                * to change the apptag.
-                                */
-                               break;
                        case SCSI_PROT_READ_STRIP:
                        case SCSI_PROT_READ_PASS:
                                /*
@@ -1502,7 +1597,13 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                                 */
                                *apptag = 0xDEAD;
                                phba->lpfc_injerr_rapp_cnt--;
-                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+                               if (phba->lpfc_injerr_rapp_cnt == 0) {
+                                       phba->lpfc_injerr_nportid = 0;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       memset(&phba->lpfc_injerr_wwpn,
+                                               0, sizeof(struct lpfc_name));
+                               }
                                rc = BG_ERR_INIT;
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
@@ -1519,57 +1620,51 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                if (phba->lpfc_injerr_wgrd_cnt) {
                        switch (op) {
                        case SCSI_PROT_WRITE_PASS:
-                               if (blockoff && src) {
-                                       /* Insert error in middle of the IO */
-
-                                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "0815 BLKGRD: Injecting guard error: "
-                                       "write lba x%lx + x%x oldgrdTag x%x\n",
-                                       (unsigned long)lba, blockoff,
-                                       src->guard_tag);
-
-                                       /*
-                                        * NOTE, this will change guard tag in
-                                        * the memory location forever!
-                                        */
-                                       src->guard_tag = 0xDEAD;
-                                       phba->lpfc_injerr_wgrd_cnt--;
-                                       phba->lpfc_injerr_lba =
-                                               LPFC_INJERR_LBA_OFF;
-                                       rc = BG_ERR_CHECK;
-                                       break;
-                               }
+                               rc = BG_ERR_CHECK;
                                /* Drop thru */
-                       case SCSI_PROT_WRITE_STRIP:
+
+                       case SCSI_PROT_WRITE_INSERT:
                                /*
-                                * For WRITE_STRIP and WRITE_PASS,
-                                * force the error on data
-                                * being copied from SLI-Host to SLI-Port.
+                                * For WRITE_INSERT, force the
+                                * error to be sent on the wire. It should be
+                                * detected by the Target.
                                 */
                                phba->lpfc_injerr_wgrd_cnt--;
-                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+                               if (phba->lpfc_injerr_wgrd_cnt == 0) {
+                                       phba->lpfc_injerr_nportid = 0;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       memset(&phba->lpfc_injerr_wwpn,
+                                               0, sizeof(struct lpfc_name));
+                               }
 
-                               rc = BG_ERR_SWAP;
+                               rc |= BG_ERR_TGT | BG_ERR_SWAP;
                                /* Signals the caller to swap CRC->CSUM */
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "0816 BLKGRD: Injecting guard error: "
+                                       "0817 BLKGRD: Injecting guard error: "
                                        "write lba x%lx\n", (unsigned long)lba);
                                break;
-                       case SCSI_PROT_WRITE_INSERT:
+                       case SCSI_PROT_WRITE_STRIP:
                                /*
-                                * For WRITE_INSERT, force the
-                                * error to be sent on the wire. It should be
-                                * detected by the Target.
+                                * For WRITE_STRIP and WRITE_PASS,
+                                * force the error on data
+                                * being copied from SLI-Host to SLI-Port.
                                 */
                                phba->lpfc_injerr_wgrd_cnt--;
-                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+                               if (phba->lpfc_injerr_wgrd_cnt == 0) {
+                                       phba->lpfc_injerr_nportid = 0;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       memset(&phba->lpfc_injerr_wwpn,
+                                               0, sizeof(struct lpfc_name));
+                               }
 
-                               rc = BG_ERR_SWAP;
+                               rc = BG_ERR_INIT | BG_ERR_SWAP;
                                /* Signals the caller to swap CRC->CSUM */
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                                       "0817 BLKGRD: Injecting guard error: "
+                                       "0816 BLKGRD: Injecting guard error: "
                                        "write lba x%lx\n", (unsigned long)lba);
                                break;
                        }
@@ -1577,11 +1672,6 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                if (phba->lpfc_injerr_rgrd_cnt) {
                        switch (op) {
                        case SCSI_PROT_READ_INSERT:
-                               /*
-                                * For READ_INSERT, it doesn't make sense
-                                * to change the guard tag.
-                                */
-                               break;
                        case SCSI_PROT_READ_STRIP:
                        case SCSI_PROT_READ_PASS:
                                /*
@@ -1589,11 +1679,16 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                                 * error on data being read off the wire. It
                                 * should force an IO error to the driver.
                                 */
-                               *apptag = 0xDEAD;
                                phba->lpfc_injerr_rgrd_cnt--;
-                               phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+                               if (phba->lpfc_injerr_rgrd_cnt == 0) {
+                                       phba->lpfc_injerr_nportid = 0;
+                                       phba->lpfc_injerr_lba =
+                                               LPFC_INJERR_LBA_OFF;
+                                       memset(&phba->lpfc_injerr_wwpn,
+                                               0, sizeof(struct lpfc_name));
+                               }
 
-                               rc = BG_ERR_SWAP;
+                               rc = BG_ERR_INIT | BG_ERR_SWAP;
                                /* Signals the caller to swap CRC->CSUM */
 
                                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
@@ -1629,20 +1724,20 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                switch (scsi_get_prot_op(sc)) {
                case SCSI_PROT_READ_INSERT:
                case SCSI_PROT_WRITE_STRIP:
-                       *txop = BG_OP_IN_CSUM_OUT_NODIF;
                        *rxop = BG_OP_IN_NODIF_OUT_CSUM;
+                       *txop = BG_OP_IN_CSUM_OUT_NODIF;
                        break;
 
                case SCSI_PROT_READ_STRIP:
                case SCSI_PROT_WRITE_INSERT:
-                       *txop = BG_OP_IN_NODIF_OUT_CRC;
                        *rxop = BG_OP_IN_CRC_OUT_NODIF;
+                       *txop = BG_OP_IN_NODIF_OUT_CRC;
                        break;
 
                case SCSI_PROT_READ_PASS:
                case SCSI_PROT_WRITE_PASS:
-                       *txop = BG_OP_IN_CSUM_OUT_CRC;
                        *rxop = BG_OP_IN_CRC_OUT_CSUM;
+                       *txop = BG_OP_IN_CSUM_OUT_CRC;
                        break;
 
                case SCSI_PROT_NORMAL:
@@ -1658,20 +1753,20 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                switch (scsi_get_prot_op(sc)) {
                case SCSI_PROT_READ_STRIP:
                case SCSI_PROT_WRITE_INSERT:
-                       *txop = BG_OP_IN_NODIF_OUT_CRC;
                        *rxop = BG_OP_IN_CRC_OUT_NODIF;
+                       *txop = BG_OP_IN_NODIF_OUT_CRC;
                        break;
 
                case SCSI_PROT_READ_PASS:
                case SCSI_PROT_WRITE_PASS:
-                       *txop = BG_OP_IN_CRC_OUT_CRC;
                        *rxop = BG_OP_IN_CRC_OUT_CRC;
+                       *txop = BG_OP_IN_CRC_OUT_CRC;
                        break;
 
                case SCSI_PROT_READ_INSERT:
                case SCSI_PROT_WRITE_STRIP:
-                       *txop = BG_OP_IN_CRC_OUT_NODIF;
                        *rxop = BG_OP_IN_NODIF_OUT_CRC;
+                       *txop = BG_OP_IN_CRC_OUT_NODIF;
                        break;
 
                case SCSI_PROT_NORMAL:
@@ -1710,20 +1805,20 @@ lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                switch (scsi_get_prot_op(sc)) {
                case SCSI_PROT_READ_INSERT:
                case SCSI_PROT_WRITE_STRIP:
-                       *txop = BG_OP_IN_CRC_OUT_NODIF;
                        *rxop = BG_OP_IN_NODIF_OUT_CRC;
+                       *txop = BG_OP_IN_CRC_OUT_NODIF;
                        break;
 
                case SCSI_PROT_READ_STRIP:
                case SCSI_PROT_WRITE_INSERT:
-                       *txop = BG_OP_IN_NODIF_OUT_CSUM;
                        *rxop = BG_OP_IN_CSUM_OUT_NODIF;
+                       *txop = BG_OP_IN_NODIF_OUT_CSUM;
                        break;
 
                case SCSI_PROT_READ_PASS:
                case SCSI_PROT_WRITE_PASS:
-                       *txop = BG_OP_IN_CRC_OUT_CRC;
-                       *rxop = BG_OP_IN_CRC_OUT_CRC;
+                       *rxop = BG_OP_IN_CSUM_OUT_CRC;
+                       *txop = BG_OP_IN_CRC_OUT_CSUM;
                        break;
 
                case SCSI_PROT_NORMAL:
@@ -1735,20 +1830,20 @@ lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                switch (scsi_get_prot_op(sc)) {
                case SCSI_PROT_READ_STRIP:
                case SCSI_PROT_WRITE_INSERT:
-                       *txop = BG_OP_IN_NODIF_OUT_CSUM;
                        *rxop = BG_OP_IN_CSUM_OUT_NODIF;
+                       *txop = BG_OP_IN_NODIF_OUT_CSUM;
                        break;
 
                case SCSI_PROT_READ_PASS:
                case SCSI_PROT_WRITE_PASS:
-                       *txop = BG_OP_IN_CSUM_OUT_CRC;
-                       *rxop = BG_OP_IN_CRC_OUT_CSUM;
+                       *rxop = BG_OP_IN_CSUM_OUT_CSUM;
+                       *txop = BG_OP_IN_CSUM_OUT_CSUM;
                        break;
 
                case SCSI_PROT_READ_INSERT:
                case SCSI_PROT_WRITE_STRIP:
-                       *txop = BG_OP_IN_CSUM_OUT_NODIF;
                        *rxop = BG_OP_IN_NODIF_OUT_CSUM;
+                       *txop = BG_OP_IN_CSUM_OUT_NODIF;
                        break;
 
                case SCSI_PROT_NORMAL:
@@ -1817,11 +1912,11 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
        reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-       rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+       rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
        if (rc) {
-               if (rc == BG_ERR_SWAP)
+               if (rc & BG_ERR_SWAP)
                        lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
-               if (rc == BG_ERR_CHECK)
+               if (rc & BG_ERR_CHECK)
                        checking = 0;
        }
 #endif
@@ -1964,11 +2059,11 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
        reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-       rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+       rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
        if (rc) {
-               if (rc == BG_ERR_SWAP)
+               if (rc & BG_ERR_SWAP)
                        lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
-               if (rc == BG_ERR_CHECK)
+               if (rc & BG_ERR_CHECK)
                        checking = 0;
        }
 #endif
@@ -2172,11 +2267,11 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
        reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-       rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+       rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
        if (rc) {
-               if (rc == BG_ERR_SWAP)
+               if (rc & BG_ERR_SWAP)
                        lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
-               if (rc == BG_ERR_CHECK)
+               if (rc & BG_ERR_CHECK)
                        checking = 0;
        }
 #endif
@@ -2312,11 +2407,11 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
        reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-       rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+       rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
        if (rc) {
-               if (rc == BG_ERR_SWAP)
+               if (rc & BG_ERR_SWAP)
                        lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
-               if (rc == BG_ERR_CHECK)
+               if (rc & BG_ERR_CHECK)
                        checking = 0;
        }
 #endif
@@ -2788,7 +2883,7 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
                /* No error was reported - problem in FW? */
                cmd->result = ScsiResult(DID_ERROR, 0);
                lpfc_printf_log(phba, KERN_ERR, LOG_BG,
-                       "9057 BLKGRD: no errors reported!\n");
+                       "9057 BLKGRD: Unknown error reported!\n");
        }
 
 out:
@@ -3460,6 +3555,37 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
        /* pick up SLI4 exhange busy status from HBA */
        lpfc_cmd->exch_busy = pIocbOut->iocb_flag & LPFC_EXCHANGE_BUSY;
 
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+       if (lpfc_cmd->prot_data_type) {
+               struct scsi_dif_tuple *src = NULL;
+
+               src =  (struct scsi_dif_tuple *)lpfc_cmd->prot_data_segment;
+               /*
+                * Used to restore any changes to protection
+                * data for error injection.
+                */
+               switch (lpfc_cmd->prot_data_type) {
+               case LPFC_INJERR_REFTAG:
+                       src->ref_tag =
+                               lpfc_cmd->prot_data;
+                       break;
+               case LPFC_INJERR_APPTAG:
+                       src->app_tag =
+                               (uint16_t)lpfc_cmd->prot_data;
+                       break;
+               case LPFC_INJERR_GUARD:
+                       src->guard_tag =
+                               (uint16_t)lpfc_cmd->prot_data;
+                       break;
+               default:
+                       break;
+               }
+
+               lpfc_cmd->prot_data = 0;
+               lpfc_cmd->prot_data_type = 0;
+               lpfc_cmd->prot_data_segment = NULL;
+       }
+#endif
        if (pnode && NLP_CHK_NODE_ACT(pnode))
                atomic_dec(&pnode->cmd_pending);
 
@@ -4061,15 +4187,6 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
                cmnd->result = err;
                goto out_fail_command;
        }
-       /*
-        * Do not let the mid-layer retry I/O too fast. If an I/O is retried
-        * without waiting a bit then indicate that the device is busy.
-        */
-       if (cmnd->retries &&
-           time_before(jiffies, (cmnd->jiffies_at_alloc +
-                                 msecs_to_jiffies(LPFC_RETRY_PAUSE *
-                                                  cmnd->retries))))
-               return SCSI_MLQUEUE_DEVICE_BUSY;
        ndlp = rdata->pnode;
 
        if ((scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) &&
@@ -4119,63 +4236,48 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
        if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
                if (vport->phba->cfg_enable_bg) {
                        lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
-                               "9033 BLKGRD: rcvd protected cmd:%02x op:%02x "
-                               "str=%s\n",
-                               cmnd->cmnd[0], scsi_get_prot_op(cmnd),
-                               dif_op_str[scsi_get_prot_op(cmnd)]);
-                       lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
-                               "9034 BLKGRD: CDB: %02x %02x %02x %02x %02x "
-                               "%02x %02x %02x %02x %02x\n",
-                               cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
-                               cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
-                               cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
-                               cmnd->cmnd[9]);
+                               "9033 BLKGRD: rcvd protected cmd:%02x op=%s "
+                               "guard=%s\n", cmnd->cmnd[0],
+                               dif_op_str[scsi_get_prot_op(cmnd)],
+                               dif_grd_str[scsi_host_get_guard(shost)]);
                        if (cmnd->cmnd[0] == READ_10)
                                lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
                                        "9035 BLKGRD: READ @ sector %llu, "
-                                       "count %u\n",
+                                       "cnt %u, rpt %d\n",
                                        (unsigned long long)scsi_get_lba(cmnd),
-                                       blk_rq_sectors(cmnd->request));
+                                       blk_rq_sectors(cmnd->request),
+                                       (cmnd->cmnd[1]>>5));
                        else if (cmnd->cmnd[0] == WRITE_10)
                                lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
                                        "9036 BLKGRD: WRITE @ sector %llu, "
-                                       "count %u cmd=%p\n",
+                                       "cnt %u, wpt %d\n",
                                        (unsigned long long)scsi_get_lba(cmnd),
                                        blk_rq_sectors(cmnd->request),
-                                       cmnd);
+                                       (cmnd->cmnd[1]>>5));
                }
 
                err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
        } else {
                if (vport->phba->cfg_enable_bg) {
                        lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
-                                       "9038 BLKGRD: rcvd unprotected cmd:"
-                                       "%02x op:%02x str=%s\n",
-                                       cmnd->cmnd[0], scsi_get_prot_op(cmnd),
-                                       dif_op_str[scsi_get_prot_op(cmnd)]);
-                               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
-                                       "9039 BLKGRD: CDB: %02x %02x %02x "
-                                       "%02x %02x %02x %02x %02x %02x %02x\n",
-                                       cmnd->cmnd[0], cmnd->cmnd[1],
-                                       cmnd->cmnd[2], cmnd->cmnd[3],
-                                       cmnd->cmnd[4], cmnd->cmnd[5],
-                                       cmnd->cmnd[6], cmnd->cmnd[7],
-                                       cmnd->cmnd[8], cmnd->cmnd[9]);
+                               "9038 BLKGRD: rcvd unprotected cmd:"
+                               "%02x op=%s guard=%s\n", cmnd->cmnd[0],
+                               dif_op_str[scsi_get_prot_op(cmnd)],
+                               dif_grd_str[scsi_host_get_guard(shost)]);
                        if (cmnd->cmnd[0] == READ_10)
                                lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
                                        "9040 dbg: READ @ sector %llu, "
-                                       "count %u\n",
+                                       "cnt %u, rpt %d\n",
                                        (unsigned long long)scsi_get_lba(cmnd),
-                                        blk_rq_sectors(cmnd->request));
+                                        blk_rq_sectors(cmnd->request),
+                                       (cmnd->cmnd[1]>>5));
                        else if (cmnd->cmnd[0] == WRITE_10)
                                lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
-                                        "9041 dbg: WRITE @ sector %llu, "
-                                        "count %u cmd=%p\n",
-                                        (unsigned long long)scsi_get_lba(cmnd),
-                                        blk_rq_sectors(cmnd->request), cmnd);
-                       else
-                               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
-                                        "9042 dbg: parser not implemented\n");
+                                       "9041 dbg: WRITE @ sector %llu, "
+                                       "cnt %u, wpt %d\n",
+                                       (unsigned long long)scsi_get_lba(cmnd),
+                                       blk_rq_sectors(cmnd->request),
+                                       (cmnd->cmnd[1]>>5));
                }
                err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
        }
index 9075a08cf78155af9ee0a635453bbd6ff9512f31..21a2ffe67eacf998089d527b736ac8f63a634ffa 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2006 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
@@ -150,9 +150,18 @@ struct lpfc_scsi_buf {
        struct lpfc_iocbq cur_iocbq;
        wait_queue_head_t *waitq;
        unsigned long start_time;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+       /* Used to restore any changes to protection data for error injection */
+       void *prot_data_segment;
+       uint32_t prot_data;
+       uint32_t prot_data_type;
+#define        LPFC_INJERR_REFTAG      1
+#define        LPFC_INJERR_APPTAG      2
+#define        LPFC_INJERR_GUARD       3
+#endif
 };
 
 #define LPFC_SCSI_DMA_EXT_SIZE 264
 #define LPFC_BPL_SIZE          1024
-#define LPFC_RETRY_PAUSE       300
 #define MDAC_DIRECT_CMD                  0x22
index e0e4d8d18244a9402ccfa5647bd07bdde8ca8576..dbaf5b963bff2f0cd07c028ddedd5b1ff0bc0bcc 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -5578,8 +5578,6 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
                for (i = 0; i < count; i++)
                        phba->sli4_hba.rpi_ids[i] = base + i;
 
-               lpfc_sli4_node_prep(phba);
-
                /* VPIs. */
                count = phba->sli4_hba.max_cfg_param.max_vpi;
                base = phba->sli4_hba.max_cfg_param.vpi_base;
@@ -5613,6 +5611,8 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
                        rc = -ENOMEM;
                        goto free_vpi_ids;
                }
+               phba->sli4_hba.max_cfg_param.xri_used = 0;
+               phba->sli4_hba.xri_count = 0;
                phba->sli4_hba.xri_ids = kzalloc(count *
                                                 sizeof(uint16_t),
                                                 GFP_KERNEL);
@@ -6147,6 +6147,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
                rc = -ENODEV;
                goto out_free_mbox;
        }
+       lpfc_sli4_node_prep(phba);
 
        /* Create all the SLI4 queues */
        rc = lpfc_sli4_queue_create(phba);
@@ -7251,11 +7252,13 @@ lpfc_sli4_post_async_mbox(struct lpfc_hba *phba)
 
 out_not_finished:
        spin_lock_irqsave(&phba->hbalock, iflags);
-       mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
-       __lpfc_mbox_cmpl_put(phba, mboxq);
-       /* Release the token */
-       psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
-       phba->sli.mbox_active = NULL;
+       if (phba->sli.mbox_active) {
+               mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
+               __lpfc_mbox_cmpl_put(phba, mboxq);
+               /* Release the token */
+               psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+               phba->sli.mbox_active = NULL;
+       }
        spin_unlock_irqrestore(&phba->hbalock, iflags);
 
        return MBX_NOT_FINISHED;
@@ -7743,6 +7746,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                        if (pcmd && (*pcmd == ELS_CMD_FLOGI ||
                                *pcmd == ELS_CMD_SCR ||
                                *pcmd == ELS_CMD_FDISC ||
+                               *pcmd == ELS_CMD_LOGO ||
                                *pcmd == ELS_CMD_PLOGI)) {
                                bf_set(els_req64_sp, &wqe->els_req, 1);
                                bf_set(els_req64_sid, &wqe->els_req,
@@ -8385,6 +8389,7 @@ lpfc_sli4_abts_err_handler(struct lpfc_hba *phba,
                           struct sli4_wcqe_xri_aborted *axri)
 {
        struct lpfc_vport *vport;
+       uint32_t ext_status = 0;
 
        if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
                lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
@@ -8396,12 +8401,20 @@ lpfc_sli4_abts_err_handler(struct lpfc_hba *phba,
        vport = ndlp->vport;
        lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
                        "3116 Port generated FCP XRI ABORT event on "
-                       "vpi %d rpi %d xri x%x status 0x%x\n",
+                       "vpi %d rpi %d xri x%x status 0x%x parameter x%x\n",
                        ndlp->vport->vpi, ndlp->nlp_rpi,
                        bf_get(lpfc_wcqe_xa_xri, axri),
-                       bf_get(lpfc_wcqe_xa_status, axri));
+                       bf_get(lpfc_wcqe_xa_status, axri),
+                       axri->parameter);
 
-       if (bf_get(lpfc_wcqe_xa_status, axri) == IOSTAT_LOCAL_REJECT)
+       /*
+        * Catch the ABTS protocol failure case.  Older OCe FW releases returned
+        * LOCAL_REJECT and 0 for a failed ABTS exchange and later OCe and
+        * LPe FW releases returned LOCAL_REJECT and SEQUENCE_TIMEOUT.
+        */
+       ext_status = axri->parameter & WCQE_PARAM_MASK;
+       if ((bf_get(lpfc_wcqe_xa_status, axri) == IOSTAT_LOCAL_REJECT) &&
+           ((ext_status == IOERR_SEQUENCE_TIMEOUT) || (ext_status == 0)))
                lpfc_sli_abts_recover_port(vport, ndlp);
 }
 
@@ -9807,12 +9820,11 @@ lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *phba)
        unsigned long timeout;
 
        timeout = msecs_to_jiffies(LPFC_MBOX_TMO * 1000) + jiffies;
+
        spin_lock_irq(&phba->hbalock);
        psli->sli_flag |= LPFC_SLI_ASYNC_MBX_BLK;
-       spin_unlock_irq(&phba->hbalock);
 
        if (psli->sli_flag & LPFC_SLI_ACTIVE) {
-               spin_lock_irq(&phba->hbalock);
                /* Determine how long we might wait for the active mailbox
                 * command to be gracefully completed by firmware.
                 */
@@ -9831,7 +9843,9 @@ lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *phba)
                                 */
                                break;
                }
-       }
+       } else
+               spin_unlock_irq(&phba->hbalock);
+
        lpfc_sli_mbox_sys_flush(phba);
 }
 
@@ -13272,7 +13286,7 @@ lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba)
        LPFC_MBOXQ_t *mbox;
        uint32_t reqlen, alloclen, index;
        uint32_t mbox_tmo;
-       uint16_t rsrc_start, rsrc_size, els_xri_cnt;
+       uint16_t rsrc_start, rsrc_size, els_xri_cnt, post_els_xri_cnt;
        uint16_t xritag_start = 0, lxri = 0;
        struct lpfc_rsrc_blks *rsrc_blk;
        int cnt, ttl_cnt, rc = 0;
@@ -13294,6 +13308,7 @@ lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba)
 
        cnt = 0;
        ttl_cnt = 0;
+       post_els_xri_cnt = els_xri_cnt;
        list_for_each_entry(rsrc_blk, &phba->sli4_hba.lpfc_xri_blk_list,
                            list) {
                rsrc_start = rsrc_blk->rsrc_start;
@@ -13303,11 +13318,12 @@ lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba)
                                "3014 Working ELS Extent start %d, cnt %d\n",
                                rsrc_start, rsrc_size);
 
-               loop_cnt = min(els_xri_cnt, rsrc_size);
-               if (ttl_cnt + loop_cnt >= els_xri_cnt) {
-                       loop_cnt = els_xri_cnt - ttl_cnt;
-                       ttl_cnt = els_xri_cnt;
-               }
+               loop_cnt = min(post_els_xri_cnt, rsrc_size);
+               if (loop_cnt < post_els_xri_cnt) {
+                       post_els_xri_cnt -= loop_cnt;
+                       ttl_cnt += loop_cnt;
+               } else
+                       ttl_cnt += post_els_xri_cnt;
 
                mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
                if (!mbox)
@@ -14203,15 +14219,14 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
                 * field and RX_ID from ABTS for RX_ID field.
                 */
                bf_set(lpfc_abts_orig, &icmd->un.bls_rsp, LPFC_ABTS_UNSOL_RSP);
-               bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, rxid);
        } else {
                /* ABTS sent by initiator to CT exchange, construction
                 * of BA_ACC will need to allocate a new XRI as for the
-                * XRI_TAG and RX_ID fields.
+                * XRI_TAG field.
                 */
                bf_set(lpfc_abts_orig, &icmd->un.bls_rsp, LPFC_ABTS_UNSOL_INT);
-               bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, NO_XRI);
        }
+       bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, rxid);
        bf_set(lpfc_abts_oxid, &icmd->un.bls_rsp, oxid);
 
        /* Xmit CT abts response on exchange <xid> */
@@ -15042,6 +15057,7 @@ lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index)
        LPFC_MBOXQ_t *mboxq;
 
        phba->fcoe_eventtag_at_fcf_scan = phba->fcoe_eventtag;
+       phba->fcoe_cvl_eventtag_attn = phba->fcoe_cvl_eventtag;
        mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
        if (!mboxq) {
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
index f2a2602e5c3528d8504f9033630be2403019c017..25cefc254b76409b414caed817cd9980c4fc786c 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
@@ -18,7 +18,7 @@
  * included with this package.                                     *
  *******************************************************************/
 
-#define LPFC_DRIVER_VERSION "8.3.29"
+#define LPFC_DRIVER_VERSION "8.3.30"
 #define LPFC_DRIVER_NAME               "lpfc"
 #define LPFC_SP_DRIVER_HANDLER_NAME    "lpfc:sp"
 #define LPFC_FP_DRIVER_HANDLER_NAME    "lpfc:fp"
index 5e69f468535f243d200aaf1a6af31f4c8b9205cb..8a59a772fdf22841ead34c937dd92e4ccb20f749 100644 (file)
@@ -657,7 +657,7 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info)
                return;
 
        /* eat the loginfos associated with task aborts */
-       if (ioc->ignore_loginfos && (log_info == 30050000 || log_info ==
+       if (ioc->ignore_loginfos && (log_info == 0x30050000 || log_info ==
            0x31140000 || log_info == 0x31130000))
                return;
 
@@ -2060,12 +2060,10 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
 {
        int i = 0;
        char desc[16];
-       u8 revision;
        u32 iounit_pg1_flags;
        u32 bios_version;
 
        bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
-       pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision);
        strncpy(desc, ioc->manu_pg0.ChipName, 16);
        printk(MPT2SAS_INFO_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), "
           "ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n",
@@ -2074,7 +2072,7 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
           (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
           (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
           ioc->facts.FWVersion.Word & 0x000000FF,
-          revision,
+          ioc->pdev->revision,
           (bios_version & 0xFF000000) >> 24,
           (bios_version & 0x00FF0000) >> 16,
           (bios_version & 0x0000FF00) >> 8,
index 7fceb899029ed990b7b19dde1c22491ad29a4dc5..3b9a28efea8281fe85b5c5c1e4757da60f5cf061 100644 (file)
@@ -1026,7 +1026,6 @@ _ctl_getiocinfo(void __user *arg)
 {
        struct mpt2_ioctl_iocinfo karg;
        struct MPT2SAS_ADAPTER *ioc;
-       u8 revision;
 
        if (copy_from_user(&karg, arg, sizeof(karg))) {
                printk(KERN_ERR "failure at %s:%d/%s()!\n",
@@ -1046,8 +1045,7 @@ _ctl_getiocinfo(void __user *arg)
                karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2;
        if (ioc->pfacts)
                karg.port_number = ioc->pfacts[0].PortNumber;
-       pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision);
-       karg.hw_rev = revision;
+       karg.hw_rev = ioc->pdev->revision;
        karg.pci_id = ioc->pdev->device;
        karg.subsystem_device = ioc->pdev->subsystem_device;
        karg.subsystem_vendor = ioc->pdev->subsystem_vendor;
index 3619f6eeeeda2b44731d80c6e8bd6c063c434911..9d82ee5c10de657572df223a11beb123709b2be7 100644 (file)
@@ -2093,6 +2093,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
        struct ata_task_resp *resp ;
        u32 *sata_resp;
        struct pm8001_device *pm8001_dev;
+       unsigned long flags;
 
        psataPayload = (struct sata_completion_resp *)(piomb + 4);
        status = le32_to_cpu(psataPayload->status);
@@ -2382,26 +2383,26 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
                ts->stat = SAS_DEV_NO_RESPONSE;
                break;
        }
-       spin_lock_irq(&t->task_state_lock);
+       spin_lock_irqsave(&t->task_state_lock, flags);
        t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
        t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
        t->task_state_flags |= SAS_TASK_STATE_DONE;
        if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
-               spin_unlock_irq(&t->task_state_lock);
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
                PM8001_FAIL_DBG(pm8001_ha,
                        pm8001_printk("task 0x%p done with io_status 0x%x"
                        " resp 0x%x stat 0x%x but aborted by upper layer!\n",
                        t, status, ts->resp, ts->stat));
                pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
        } else if (t->uldd_task) {
-               spin_unlock_irq(&t->task_state_lock);
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
                pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
                mb();/* ditto */
                spin_unlock_irq(&pm8001_ha->lock);
                t->task_done(t);
                spin_lock_irq(&pm8001_ha->lock);
        } else if (!t->uldd_task) {
-               spin_unlock_irq(&t->task_state_lock);
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
                pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
                mb();/*ditto*/
                spin_unlock_irq(&pm8001_ha->lock);
@@ -2423,6 +2424,7 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
        u32 tag = le32_to_cpu(psataPayload->tag);
        u32 port_id = le32_to_cpu(psataPayload->port_id);
        u32 dev_id = le32_to_cpu(psataPayload->device_id);
+       unsigned long flags;
 
        ccb = &pm8001_ha->ccb_info[tag];
        t = ccb->task;
@@ -2593,26 +2595,26 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
                ts->stat = SAS_OPEN_TO;
                break;
        }
-       spin_lock_irq(&t->task_state_lock);
+       spin_lock_irqsave(&t->task_state_lock, flags);
        t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
        t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
        t->task_state_flags |= SAS_TASK_STATE_DONE;
        if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
-               spin_unlock_irq(&t->task_state_lock);
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
                PM8001_FAIL_DBG(pm8001_ha,
                        pm8001_printk("task 0x%p done with io_status 0x%x"
                        " resp 0x%x stat 0x%x but aborted by upper layer!\n",
                        t, event, ts->resp, ts->stat));
                pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
        } else if (t->uldd_task) {
-               spin_unlock_irq(&t->task_state_lock);
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
                pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
                mb();/* ditto */
                spin_unlock_irq(&pm8001_ha->lock);
                t->task_done(t);
                spin_lock_irq(&pm8001_ha->lock);
        } else if (!t->uldd_task) {
-               spin_unlock_irq(&t->task_state_lock);
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
                pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
                mb();/*ditto*/
                spin_unlock_irq(&pm8001_ha->lock);
index 7c9f28b7da7283c33741d6f6ae6377e7b71ead79..fc542a9bb106231ac9f4991388b2f9ee1e3d0d46 100644 (file)
@@ -431,9 +431,9 @@ static void qla4xxx_mbox_status_entry(struct scsi_qla_host *ha,
                                  mbox_sts_entry->out_mbox[6]));
 
                if (mbox_sts_entry->out_mbox[0] == MBOX_STS_COMMAND_COMPLETE)
-                       status = QLA_SUCCESS;
+                       status = ISCSI_PING_SUCCESS;
                else
-                       status = QLA_ERROR;
+                       status = mbox_sts_entry->out_mbox[6];
 
                data_size = sizeof(mbox_sts_entry->out_mbox);
 
index 3d9419460e0c5742177b290c6d31bbe0790a2212..ee47820c30a6591824cfa42abff426fb09114e7b 100644 (file)
@@ -834,7 +834,7 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc)
 static void qla4xxx_set_port_speed(struct Scsi_Host *shost)
 {
        struct scsi_qla_host *ha = to_qla_host(shost);
-       struct iscsi_cls_host *ihost = shost_priv(shost);
+       struct iscsi_cls_host *ihost = shost->shost_data;
        uint32_t speed = ISCSI_PORT_SPEED_UNKNOWN;
 
        qla4xxx_get_firmware_state(ha);
@@ -859,7 +859,7 @@ static void qla4xxx_set_port_speed(struct Scsi_Host *shost)
 static void qla4xxx_set_port_state(struct Scsi_Host *shost)
 {
        struct scsi_qla_host *ha = to_qla_host(shost);
-       struct iscsi_cls_host *ihost = shost_priv(shost);
+       struct iscsi_cls_host *ihost = shost->shost_data;
        uint32_t state = ISCSI_PORT_STATE_DOWN;
 
        if (test_bit(AF_LINK_UP, &ha->flags))
@@ -3445,7 +3445,6 @@ static void qla4xxx_free_adapter(struct scsi_qla_host *ha)
 int qla4_8xxx_iospace_config(struct scsi_qla_host *ha)
 {
        int status = 0;
-       uint8_t revision_id;
        unsigned long mem_base, mem_len, db_base, db_len;
        struct pci_dev *pdev = ha->pdev;
 
@@ -3457,10 +3456,9 @@ int qla4_8xxx_iospace_config(struct scsi_qla_host *ha)
                goto iospace_error_exit;
        }
 
-       pci_read_config_byte(pdev, PCI_REVISION_ID, &revision_id);
        DEBUG2(printk(KERN_INFO "%s: revision-id=%d\n",
-           __func__, revision_id));
-       ha->revision_id = revision_id;
+           __func__, pdev->revision));
+       ha->revision_id = pdev->revision;
 
        /* remap phys address */
        mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
index ede9af9441417e6740aff7d68d4bf8aa0a48f45f..97b30c108e365f6d22e93fc4ca826b6eb46c5e34 100644 (file)
@@ -5,4 +5,4 @@
  * See LICENSE.qla4xxx for copyright and licensing details.
  */
 
-#define QLA4XXX_DRIVER_VERSION "5.02.00-k15"
+#define QLA4XXX_DRIVER_VERSION "5.02.00-k16"
index 591856131c4e14b96cf537a1d4e9c05bc235c6ee..182d5a57ab7468a6ddf5c6bfb684bf1396cb7f7b 100644 (file)
@@ -101,6 +101,7 @@ static const char * scsi_debug_version_date = "20100324";
 #define DEF_LBPU 0
 #define DEF_LBPWS 0
 #define DEF_LBPWS10 0
+#define DEF_LBPRZ 1
 #define DEF_LOWEST_ALIGNED 0
 #define DEF_NO_LUN_0   0
 #define DEF_NUM_PARTS   0
@@ -186,6 +187,7 @@ static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
 static unsigned int scsi_debug_lbpu = DEF_LBPU;
 static unsigned int scsi_debug_lbpws = DEF_LBPWS;
 static unsigned int scsi_debug_lbpws10 = DEF_LBPWS10;
+static unsigned int scsi_debug_lbprz = DEF_LBPRZ;
 static unsigned int scsi_debug_unmap_alignment = DEF_UNMAP_ALIGNMENT;
 static unsigned int scsi_debug_unmap_granularity = DEF_UNMAP_GRANULARITY;
 static unsigned int scsi_debug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS;
@@ -775,10 +777,10 @@ static int inquiry_evpd_b1(unsigned char *arr)
        return 0x3c;
 }
 
-/* Thin provisioning VPD page (SBC-3) */
+/* Logical block provisioning VPD page (SBC-3) */
 static int inquiry_evpd_b2(unsigned char *arr)
 {
-       memset(arr, 0, 0x8);
+       memset(arr, 0, 0x4);
        arr[0] = 0;                     /* threshold exponent */
 
        if (scsi_debug_lbpu)
@@ -790,7 +792,10 @@ static int inquiry_evpd_b2(unsigned char *arr)
        if (scsi_debug_lbpws10)
                arr[1] |= 1 << 5;
 
-       return 0x8;
+       if (scsi_debug_lbprz)
+               arr[1] |= 1 << 2;
+
+       return 0x4;
 }
 
 #define SDEBUG_LONG_INQ_SZ 96
@@ -1071,8 +1076,11 @@ static int resp_readcap16(struct scsi_cmnd * scp,
        arr[13] = scsi_debug_physblk_exp & 0xf;
        arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f;
 
-       if (scsi_debug_lbp())
+       if (scsi_debug_lbp()) {
                arr[14] |= 0x80; /* LBPME */
+               if (scsi_debug_lbprz)
+                       arr[14] |= 0x40; /* LBPRZ */
+       }
 
        arr[15] = scsi_debug_lowest_aligned & 0xff;
 
@@ -2046,10 +2054,13 @@ static void unmap_region(sector_t lba, unsigned int len)
                block = lba + alignment;
                rem = do_div(block, granularity);
 
-               if (rem == 0 && lba + granularity <= end &&
-                   block < map_size)
+               if (rem == 0 && lba + granularity <= end && block < map_size) {
                        clear_bit(block, map_storep);
-
+                       if (scsi_debug_lbprz)
+                               memset(fake_storep +
+                                      block * scsi_debug_sector_size, 0,
+                                      scsi_debug_sector_size);
+               }
                lba += granularity - rem;
        }
 }
@@ -2731,6 +2742,7 @@ module_param_named(guard, scsi_debug_guard, int, S_IRUGO);
 module_param_named(lbpu, scsi_debug_lbpu, int, S_IRUGO);
 module_param_named(lbpws, scsi_debug_lbpws, int, S_IRUGO);
 module_param_named(lbpws10, scsi_debug_lbpws10, int, S_IRUGO);
+module_param_named(lbprz, scsi_debug_lbprz, int, S_IRUGO);
 module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO);
 module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
 module_param_named(max_queue, scsi_debug_max_queue, int, S_IRUGO | S_IWUSR);
@@ -2772,6 +2784,7 @@ MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)");
 MODULE_PARM_DESC(lbpu, "enable LBP, support UNMAP command (def=0)");
 MODULE_PARM_DESC(lbpws, "enable LBP, support WRITE SAME(16) with UNMAP bit (def=0)");
 MODULE_PARM_DESC(lbpws10, "enable LBP, support WRITE SAME(10) with UNMAP bit (def=0)");
+MODULE_PARM_DESC(lbprz, "unmapped blocks return 0 on read (def=1)");
 MODULE_PARM_DESC(lowest_aligned, "lowest aligned lba (def=0)");
 MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
 MODULE_PARM_DESC(max_queue, "max number of queued commands (1 to 255(def))");
index fac31730addfd986990055c0baa8ef092fe9b797..1cf640e575da4567fb22516bc5eff3cf3ff0efe9 100644 (file)
@@ -1486,7 +1486,7 @@ void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport,
        struct iscsi_uevent *ev;
        int len = NLMSG_SPACE(sizeof(*ev) + data_size);
 
-       skb = alloc_skb(len, GFP_KERNEL);
+       skb = alloc_skb(len, GFP_NOIO);
        if (!skb) {
                printk(KERN_ERR "gracefully ignored host event (%d):%d OOM\n",
                       host_no, code);
@@ -1504,7 +1504,7 @@ void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport,
        if (data_size)
                memcpy((char *)ev + sizeof(*ev), data, data_size);
 
-       iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
+       iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_NOIO);
 }
 EXPORT_SYMBOL_GPL(iscsi_post_host_event);
 
@@ -1517,7 +1517,7 @@ void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
        struct iscsi_uevent *ev;
        int len = NLMSG_SPACE(sizeof(*ev) + data_size);
 
-       skb = alloc_skb(len, GFP_KERNEL);
+       skb = alloc_skb(len, GFP_NOIO);
        if (!skb) {
                printk(KERN_ERR "gracefully ignored ping comp: OOM\n");
                return;
@@ -1533,7 +1533,7 @@ void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
        ev->r.ping_comp.data_size = data_size;
        memcpy((char *)ev + sizeof(*ev), data, data_size);
 
-       iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
+       iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_NOIO);
 }
 EXPORT_SYMBOL_GPL(iscsi_ping_comp_event);
 
index 09e3df42a4024925a69836d7c2a0d3a3149815e9..5ba5c2a9e8e987ffe95da22a179d42b18590570e 100644 (file)
@@ -664,7 +664,7 @@ static void sd_unprep_fn(struct request_queue *q, struct request *rq)
 }
 
 /**
- *     sd_init_command - build a scsi (read or write) command from
+ *     sd_prep_fn - build a scsi (read or write) command from
  *     information in the request structure.
  *     @SCpnt: pointer to mid-level's per scsi command structure that
  *     contains request and into which the scsi command is written
@@ -711,7 +711,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
        ret = BLKPREP_KILL;
 
        SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt,
-                                       "sd_init_command: block=%llu, "
+                                       "sd_prep_fn: block=%llu, "
                                        "count=%d\n",
                                        (unsigned long long)block,
                                        this_count));
@@ -1212,9 +1212,14 @@ static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing)
        retval = -ENODEV;
 
        if (scsi_block_when_processing_errors(sdp)) {
+               retval = scsi_autopm_get_device(sdp);
+               if (retval)
+                       goto out;
+
                sshdr  = kzalloc(sizeof(*sshdr), GFP_KERNEL);
                retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
                                              sshdr);
+               scsi_autopm_put_device(sdp);
        }
 
        /* failed to execute TUR, assume media not present */
@@ -2644,8 +2649,8 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
  *     (e.g. /dev/sda). More precisely it is the block device major 
  *     and minor number that is chosen here.
  *
- *     Assume sd_attach is not re-entrant (for time being)
- *     Also think about sd_attach() and sd_remove() running coincidentally.
+ *     Assume sd_probe is not re-entrant (for time being)
+ *     Also think about sd_probe() and sd_remove() running coincidentally.
  **/
 static int sd_probe(struct device *dev)
 {
@@ -2660,7 +2665,7 @@ static int sd_probe(struct device *dev)
                goto out;
 
        SCSI_LOG_HLQUEUE(3, sdev_printk(KERN_INFO, sdp,
-                                       "sd_attach\n"));
+                                       "sd_probe\n"));
 
        error = -ENOMEM;
        sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL);
index a15f691f9d3423e270f17b1267e81ba1b105f252..e41998cb098ebbea2a6243bda198cc463f857a35 100644 (file)
@@ -1105,6 +1105,12 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
                                     STp->drv_buffer));
                }
                STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
+               if (!STp->drv_buffer && STp->immediate_filemark) {
+                       printk(KERN_WARNING
+                           "%s: non-buffered tape: disabling writing immediate filemarks\n",
+                           name);
+                       STp->immediate_filemark = 0;
+               }
        }
        st_release_request(SRpnt);
        SRpnt = NULL;
@@ -1313,6 +1319,8 @@ static int st_flush(struct file *filp, fl_owner_t id)
 
                memset(cmd, 0, MAX_COMMAND_SIZE);
                cmd[0] = WRITE_FILEMARKS;
+               if (STp->immediate_filemark)
+                       cmd[1] = 1;
                cmd[4] = 1 + STp->two_fm;
 
                SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
@@ -2180,8 +2188,9 @@ static void st_log_options(struct scsi_tape * STp, struct st_modedef * STm, char
                       name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
                       STp->scsi2_logical);
                printk(KERN_INFO
-                      "%s:    sysv: %d nowait: %d sili: %d\n", name, STm->sysv, STp->immediate,
-                       STp->sili);
+                      "%s:    sysv: %d nowait: %d sili: %d nowait_filemark: %d\n",
+                      name, STm->sysv, STp->immediate, STp->sili,
+                      STp->immediate_filemark);
                printk(KERN_INFO "%s:    debugging: %d\n",
                       name, debugging);
        }
@@ -2223,6 +2232,7 @@ static int st_set_options(struct scsi_tape *STp, long options)
                        STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
                STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
                STp->immediate = (options & MT_ST_NOWAIT) != 0;
+               STp->immediate_filemark = (options & MT_ST_NOWAIT_EOF) != 0;
                STm->sysv = (options & MT_ST_SYSV) != 0;
                STp->sili = (options & MT_ST_SILI) != 0;
                DEB( debugging = (options & MT_ST_DEBUGGING) != 0;
@@ -2254,6 +2264,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
                        STp->scsi2_logical = value;
                if ((options & MT_ST_NOWAIT) != 0)
                        STp->immediate = value;
+               if ((options & MT_ST_NOWAIT_EOF) != 0)
+                       STp->immediate_filemark = value;
                if ((options & MT_ST_SYSV) != 0)
                        STm->sysv = value;
                if ((options & MT_ST_SILI) != 0)
@@ -2713,7 +2725,8 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
                cmd[0] = WRITE_FILEMARKS;
                if (cmd_in == MTWSM)
                        cmd[1] = 2;
-               if (cmd_in == MTWEOFI)
+               if (cmd_in == MTWEOFI ||
+                   (cmd_in == MTWEOF && STp->immediate_filemark))
                        cmd[1] |= 1;
                cmd[2] = (arg >> 16);
                cmd[3] = (arg >> 8);
@@ -4092,6 +4105,7 @@ static int st_probe(struct device *dev)
        tpnt->scsi2_logical = ST_SCSI2LOGICAL;
        tpnt->sili = ST_SILI;
        tpnt->immediate = ST_NOWAIT;
+       tpnt->immediate_filemark = 0;
        tpnt->default_drvbuffer = 0xff;         /* No forced buffering */
        tpnt->partition = 0;
        tpnt->new_partition = 0;
@@ -4477,6 +4491,7 @@ st_options_show(struct device *dev, struct device_attribute *attr, char *buf)
        options |= STp->scsi2_logical ? MT_ST_SCSI2LOGICAL : 0;
        options |= STm->sysv ? MT_ST_SYSV : 0;
        options |= STp->immediate ? MT_ST_NOWAIT : 0;
+       options |= STp->immediate_filemark ? MT_ST_NOWAIT_EOF : 0;
        options |= STp->sili ? MT_ST_SILI : 0;
 
        l = snprintf(buf, PAGE_SIZE, "0x%08x\n", options);
index f91a67c6d9686c07646538e3dbd5745a6ca7fefe..ea35632b986c555f17281e8dcf944b26ddbcc761 100644 (file)
@@ -120,6 +120,7 @@ struct scsi_tape {
        unsigned char c_algo;                   /* compression algorithm */
        unsigned char pos_unknown;                      /* after reset position unknown */
        unsigned char sili;                     /* use SILI when reading in variable b mode */
+       unsigned char immediate_filemark;       /* write filemark immediately */
        int tape_type;
        int long_timeout;       /* timeout for commands known to take long time */
 
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
new file mode 100644 (file)
index 0000000..8f27f9d
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Kernel configuration file for the UFS Host Controller
+#
+# This code is based on drivers/scsi/ufs/Kconfig
+# Copyright (C) 2011  Samsung Samsung India Software Operations
+#
+# Santosh Yaraganavi <santosh.sy@samsung.com>
+# Vinayak Holikatti <h.vinayak@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.
+
+# 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.
+
+# NO WARRANTY
+# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+# solely responsible for determining the appropriateness of using and
+# distributing the Program and assumes all risks associated with its
+# exercise of rights under this Agreement, including but not limited to
+# the risks and costs of program errors, damage to or loss of data,
+# programs or equipment, and unavailability or interruption of operations.
+
+# DISCLAIMER OF LIABILITY
+# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+# 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 Street, Fifth Floor, Boston, MA  02110-1301,
+# USA.
+
+config SCSI_UFSHCD
+       tristate "Universal Flash Storage host controller driver"
+       depends on PCI && SCSI
+       ---help---
+       This is a generic driver which supports PCIe UFS Host controllers.
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
new file mode 100644 (file)
index 0000000..adf7895
--- /dev/null
@@ -0,0 +1,2 @@
+# UFSHCD makefile
+obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
new file mode 100644 (file)
index 0000000..b207529
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Universal Flash Storage Host controller driver
+ *
+ * This code is based on drivers/scsi/ufs/ufs.h
+ * Copyright (C) 2011-2012 Samsung India Software Operations
+ *
+ * Santosh Yaraganavi <santosh.sy@samsung.com>
+ * Vinayak Holikatti <h.vinayak@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.
+ *
+ * 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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * 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 Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ */
+
+#ifndef _UFS_H
+#define _UFS_H
+
+#define MAX_CDB_SIZE   16
+
+#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
+                       ((byte3 << 24) | (byte2 << 16) |\
+                        (byte1 << 8) | (byte0))
+
+/*
+ * UFS Protocol Information Unit related definitions
+ */
+
+/* Task management functions */
+enum {
+       UFS_ABORT_TASK          = 0x01,
+       UFS_ABORT_TASK_SET      = 0x02,
+       UFS_CLEAR_TASK_SET      = 0x04,
+       UFS_LOGICAL_RESET       = 0x08,
+       UFS_QUERY_TASK          = 0x80,
+       UFS_QUERY_TASK_SET      = 0x81,
+};
+
+/* UTP UPIU Transaction Codes Initiator to Target */
+enum {
+       UPIU_TRANSACTION_NOP_OUT        = 0x00,
+       UPIU_TRANSACTION_COMMAND        = 0x01,
+       UPIU_TRANSACTION_DATA_OUT       = 0x02,
+       UPIU_TRANSACTION_TASK_REQ       = 0x04,
+       UPIU_TRANSACTION_QUERY_REQ      = 0x26,
+};
+
+/* UTP UPIU Transaction Codes Target to Initiator */
+enum {
+       UPIU_TRANSACTION_NOP_IN         = 0x20,
+       UPIU_TRANSACTION_RESPONSE       = 0x21,
+       UPIU_TRANSACTION_DATA_IN        = 0x22,
+       UPIU_TRANSACTION_TASK_RSP       = 0x24,
+       UPIU_TRANSACTION_READY_XFER     = 0x31,
+       UPIU_TRANSACTION_QUERY_RSP      = 0x36,
+};
+
+/* UPIU Read/Write flags */
+enum {
+       UPIU_CMD_FLAGS_NONE     = 0x00,
+       UPIU_CMD_FLAGS_WRITE    = 0x20,
+       UPIU_CMD_FLAGS_READ     = 0x40,
+};
+
+/* UPIU Task Attributes */
+enum {
+       UPIU_TASK_ATTR_SIMPLE   = 0x00,
+       UPIU_TASK_ATTR_ORDERED  = 0x01,
+       UPIU_TASK_ATTR_HEADQ    = 0x02,
+       UPIU_TASK_ATTR_ACA      = 0x03,
+};
+
+/* UTP QUERY Transaction Specific Fields OpCode */
+enum {
+       UPIU_QUERY_OPCODE_NOP           = 0x0,
+       UPIU_QUERY_OPCODE_READ_DESC     = 0x1,
+       UPIU_QUERY_OPCODE_WRITE_DESC    = 0x2,
+       UPIU_QUERY_OPCODE_READ_ATTR     = 0x3,
+       UPIU_QUERY_OPCODE_WRITE_ATTR    = 0x4,
+       UPIU_QUERY_OPCODE_READ_FLAG     = 0x5,
+       UPIU_QUERY_OPCODE_SET_FLAG      = 0x6,
+       UPIU_QUERY_OPCODE_CLEAR_FLAG    = 0x7,
+       UPIU_QUERY_OPCODE_TOGGLE_FLAG   = 0x8,
+};
+
+/* UTP Transfer Request Command Type (CT) */
+enum {
+       UPIU_COMMAND_SET_TYPE_SCSI      = 0x0,
+       UPIU_COMMAND_SET_TYPE_UFS       = 0x1,
+       UPIU_COMMAND_SET_TYPE_QUERY     = 0x2,
+};
+
+enum {
+       MASK_SCSI_STATUS        = 0xFF,
+       MASK_TASK_RESPONSE      = 0xFF00,
+       MASK_RSP_UPIU_RESULT    = 0xFFFF,
+};
+
+/* Task management service response */
+enum {
+       UPIU_TASK_MANAGEMENT_FUNC_COMPL         = 0x00,
+       UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04,
+       UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED     = 0x08,
+       UPIU_TASK_MANAGEMENT_FUNC_FAILED        = 0x05,
+       UPIU_INCORRECT_LOGICAL_UNIT_NO          = 0x09,
+};
+/**
+ * struct utp_upiu_header - UPIU header structure
+ * @dword_0: UPIU header DW-0
+ * @dword_1: UPIU header DW-1
+ * @dword_2: UPIU header DW-2
+ */
+struct utp_upiu_header {
+       u32 dword_0;
+       u32 dword_1;
+       u32 dword_2;
+};
+
+/**
+ * struct utp_upiu_cmd - Command UPIU structure
+ * @header: UPIU header structure DW-0 to DW-2
+ * @data_transfer_len: Data Transfer Length DW-3
+ * @cdb: Command Descriptor Block CDB DW-4 to DW-7
+ */
+struct utp_upiu_cmd {
+       struct utp_upiu_header header;
+       u32 exp_data_transfer_len;
+       u8 cdb[MAX_CDB_SIZE];
+};
+
+/**
+ * struct utp_upiu_rsp - Response UPIU structure
+ * @header: UPIU header DW-0 to DW-2
+ * @residual_transfer_count: Residual transfer count DW-3
+ * @reserved: Reserved double words DW-4 to DW-7
+ * @sense_data_len: Sense data length DW-8 U16
+ * @sense_data: Sense data field DW-8 to DW-12
+ */
+struct utp_upiu_rsp {
+       struct utp_upiu_header header;
+       u32 residual_transfer_count;
+       u32 reserved[4];
+       u16 sense_data_len;
+       u8 sense_data[18];
+};
+
+/**
+ * struct utp_upiu_task_req - Task request UPIU structure
+ * @header - UPIU header structure DW0 to DW-2
+ * @input_param1: Input parameter 1 DW-3
+ * @input_param2: Input parameter 2 DW-4
+ * @input_param3: Input parameter 3 DW-5
+ * @reserved: Reserved double words DW-6 to DW-7
+ */
+struct utp_upiu_task_req {
+       struct utp_upiu_header header;
+       u32 input_param1;
+       u32 input_param2;
+       u32 input_param3;
+       u32 reserved[2];
+};
+
+/**
+ * struct utp_upiu_task_rsp - Task Management Response UPIU structure
+ * @header: UPIU header structure DW0-DW-2
+ * @output_param1: Ouput parameter 1 DW3
+ * @output_param2: Output parameter 2 DW4
+ * @reserved: Reserved double words DW-5 to DW-7
+ */
+struct utp_upiu_task_rsp {
+       struct utp_upiu_header header;
+       u32 output_param1;
+       u32 output_param2;
+       u32 reserved[3];
+};
+
+#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
new file mode 100644 (file)
index 0000000..52b96e8
--- /dev/null
@@ -0,0 +1,1978 @@
+/*
+ * Universal Flash Storage Host controller driver
+ *
+ * This code is based on drivers/scsi/ufs/ufshcd.c
+ * Copyright (C) 2011-2012 Samsung India Software Operations
+ *
+ * Santosh Yaraganavi <santosh.sy@samsung.com>
+ * Vinayak Holikatti <h.vinayak@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.
+ *
+ * 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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * 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 Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+
+#include "ufs.h"
+#include "ufshci.h"
+
+#define UFSHCD "ufshcd"
+#define UFSHCD_DRIVER_VERSION "0.1"
+
+enum {
+       UFSHCD_MAX_CHANNEL      = 0,
+       UFSHCD_MAX_ID           = 1,
+       UFSHCD_MAX_LUNS         = 8,
+       UFSHCD_CMD_PER_LUN      = 32,
+       UFSHCD_CAN_QUEUE        = 32,
+};
+
+/* UFSHCD states */
+enum {
+       UFSHCD_STATE_OPERATIONAL,
+       UFSHCD_STATE_RESET,
+       UFSHCD_STATE_ERROR,
+};
+
+/* Interrupt configuration options */
+enum {
+       UFSHCD_INT_DISABLE,
+       UFSHCD_INT_ENABLE,
+       UFSHCD_INT_CLEAR,
+};
+
+/* Interrupt aggregation options */
+enum {
+       INT_AGGR_RESET,
+       INT_AGGR_CONFIG,
+};
+
+/**
+ * struct uic_command - UIC command structure
+ * @command: UIC command
+ * @argument1: UIC command argument 1
+ * @argument2: UIC command argument 2
+ * @argument3: UIC command argument 3
+ * @cmd_active: Indicate if UIC command is outstanding
+ * @result: UIC command result
+ */
+struct uic_command {
+       u32 command;
+       u32 argument1;
+       u32 argument2;
+       u32 argument3;
+       int cmd_active;
+       int result;
+};
+
+/**
+ * struct ufs_hba - per adapter private structure
+ * @mmio_base: UFSHCI base register address
+ * @ucdl_base_addr: UFS Command Descriptor base address
+ * @utrdl_base_addr: UTP Transfer Request Descriptor base address
+ * @utmrdl_base_addr: UTP Task Management Descriptor base address
+ * @ucdl_dma_addr: UFS Command Descriptor DMA address
+ * @utrdl_dma_addr: UTRDL DMA address
+ * @utmrdl_dma_addr: UTMRDL DMA address
+ * @host: Scsi_Host instance of the driver
+ * @pdev: PCI device handle
+ * @lrb: local reference block
+ * @outstanding_tasks: Bits representing outstanding task requests
+ * @outstanding_reqs: Bits representing outstanding transfer requests
+ * @capabilities: UFS Controller Capabilities
+ * @nutrs: Transfer Request Queue depth supported by controller
+ * @nutmrs: Task Management Queue depth supported by controller
+ * @active_uic_cmd: handle of active UIC command
+ * @ufshcd_tm_wait_queue: wait queue for task management
+ * @tm_condition: condition variable for task management
+ * @ufshcd_state: UFSHCD states
+ * @int_enable_mask: Interrupt Mask Bits
+ * @uic_workq: Work queue for UIC completion handling
+ * @feh_workq: Work queue for fatal controller error handling
+ * @errors: HBA errors
+ */
+struct ufs_hba {
+       void __iomem *mmio_base;
+
+       /* Virtual memory reference */
+       struct utp_transfer_cmd_desc *ucdl_base_addr;
+       struct utp_transfer_req_desc *utrdl_base_addr;
+       struct utp_task_req_desc *utmrdl_base_addr;
+
+       /* DMA memory reference */
+       dma_addr_t ucdl_dma_addr;
+       dma_addr_t utrdl_dma_addr;
+       dma_addr_t utmrdl_dma_addr;
+
+       struct Scsi_Host *host;
+       struct pci_dev *pdev;
+
+       struct ufshcd_lrb *lrb;
+
+       unsigned long outstanding_tasks;
+       unsigned long outstanding_reqs;
+
+       u32 capabilities;
+       int nutrs;
+       int nutmrs;
+       u32 ufs_version;
+
+       struct uic_command active_uic_cmd;
+       wait_queue_head_t ufshcd_tm_wait_queue;
+       unsigned long tm_condition;
+
+       u32 ufshcd_state;
+       u32 int_enable_mask;
+
+       /* Work Queues */
+       struct work_struct uic_workq;
+       struct work_struct feh_workq;
+
+       /* HBA Errors */
+       u32 errors;
+};
+
+/**
+ * struct ufshcd_lrb - local reference block
+ * @utr_descriptor_ptr: UTRD address of the command
+ * @ucd_cmd_ptr: UCD address of the command
+ * @ucd_rsp_ptr: Response UPIU address for this command
+ * @ucd_prdt_ptr: PRDT address of the command
+ * @cmd: pointer to SCSI command
+ * @sense_buffer: pointer to sense buffer address of the SCSI command
+ * @sense_bufflen: Length of the sense buffer
+ * @scsi_status: SCSI status of the command
+ * @command_type: SCSI, UFS, Query.
+ * @task_tag: Task tag of the command
+ * @lun: LUN of the command
+ */
+struct ufshcd_lrb {
+       struct utp_transfer_req_desc *utr_descriptor_ptr;
+       struct utp_upiu_cmd *ucd_cmd_ptr;
+       struct utp_upiu_rsp *ucd_rsp_ptr;
+       struct ufshcd_sg_entry *ucd_prdt_ptr;
+
+       struct scsi_cmnd *cmd;
+       u8 *sense_buffer;
+       unsigned int sense_bufflen;
+       int scsi_status;
+
+       int command_type;
+       int task_tag;
+       unsigned int lun;
+};
+
+/**
+ * ufshcd_get_ufs_version - Get the UFS version supported by the HBA
+ * @hba - Pointer to adapter instance
+ *
+ * Returns UFSHCI version supported by the controller
+ */
+static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
+{
+       return readl(hba->mmio_base + REG_UFS_VERSION);
+}
+
+/**
+ * ufshcd_is_device_present - Check if any device connected to
+ *                           the host controller
+ * @reg_hcs - host controller status register value
+ *
+ * Returns 0 if device present, non-zero if no device detected
+ */
+static inline int ufshcd_is_device_present(u32 reg_hcs)
+{
+       return (DEVICE_PRESENT & reg_hcs) ? 0 : -1;
+}
+
+/**
+ * ufshcd_get_tr_ocs - Get the UTRD Overall Command Status
+ * @lrb: pointer to local command reference block
+ *
+ * This function is used to get the OCS field from UTRD
+ * Returns the OCS field in the UTRD
+ */
+static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
+{
+       return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS;
+}
+
+/**
+ * ufshcd_get_tmr_ocs - Get the UTMRD Overall Command Status
+ * @task_req_descp: pointer to utp_task_req_desc structure
+ *
+ * This function is used to get the OCS field from UTMRD
+ * Returns the OCS field in the UTMRD
+ */
+static inline int
+ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
+{
+       return task_req_descp->header.dword_2 & MASK_OCS;
+}
+
+/**
+ * ufshcd_get_tm_free_slot - get a free slot for task management request
+ * @hba: per adapter instance
+ *
+ * Returns maximum number of task management request slots in case of
+ * task management queue full or returns the free slot number
+ */
+static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba)
+{
+       return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs);
+}
+
+/**
+ * ufshcd_utrl_clear - Clear a bit in UTRLCLR register
+ * @hba: per adapter instance
+ * @pos: position of the bit to be cleared
+ */
+static inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 pos)
+{
+       writel(~(1 << pos),
+               (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_CLEAR));
+}
+
+/**
+ * ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
+ * @reg: Register value of host controller status
+ *
+ * Returns integer, 0 on Success and positive value if failed
+ */
+static inline int ufshcd_get_lists_status(u32 reg)
+{
+       /*
+        * The mask 0xFF is for the following HCS register bits
+        * Bit          Description
+        *  0           Device Present
+        *  1           UTRLRDY
+        *  2           UTMRLRDY
+        *  3           UCRDY
+        *  4           HEI
+        *  5           DEI
+        * 6-7          reserved
+        */
+       return (((reg) & (0xFF)) >> 1) ^ (0x07);
+}
+
+/**
+ * ufshcd_get_uic_cmd_result - Get the UIC command result
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the result of UIC command completion
+ * Returns 0 on success, non zero value on error
+ */
+static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
+{
+       return readl(hba->mmio_base + REG_UIC_COMMAND_ARG_2) &
+              MASK_UIC_COMMAND_RESULT;
+}
+
+/**
+ * ufshcd_free_hba_memory - Free allocated memory for LRB, request
+ *                         and task lists
+ * @hba: Pointer to adapter instance
+ */
+static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
+{
+       size_t utmrdl_size, utrdl_size, ucdl_size;
+
+       kfree(hba->lrb);
+
+       if (hba->utmrdl_base_addr) {
+               utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs;
+               dma_free_coherent(&hba->pdev->dev, utmrdl_size,
+                                 hba->utmrdl_base_addr, hba->utmrdl_dma_addr);
+       }
+
+       if (hba->utrdl_base_addr) {
+               utrdl_size =
+               (sizeof(struct utp_transfer_req_desc) * hba->nutrs);
+               dma_free_coherent(&hba->pdev->dev, utrdl_size,
+                                 hba->utrdl_base_addr, hba->utrdl_dma_addr);
+       }
+
+       if (hba->ucdl_base_addr) {
+               ucdl_size =
+               (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs);
+               dma_free_coherent(&hba->pdev->dev, ucdl_size,
+                                 hba->ucdl_base_addr, hba->ucdl_dma_addr);
+       }
+}
+
+/**
+ * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * This function checks the response UPIU for valid transaction type in
+ * response field
+ * Returns 0 on success, non-zero on failure
+ */
+static inline int
+ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+       return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
+                UPIU_TRANSACTION_RESPONSE) ? 0 : DID_ERROR << 16;
+}
+
+/**
+ * ufshcd_get_rsp_upiu_result - Get the result from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * This function gets the response status and scsi_status from response UPIU
+ * Returns the response result code.
+ */
+static inline int
+ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+       return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
+}
+
+/**
+ * ufshcd_config_int_aggr - Configure interrupt aggregation values.
+ *             Currently there is no use case where we want to configure
+ *             interrupt aggregation dynamically. So to configure interrupt
+ *             aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
+ *             INT_AGGR_TIMEOUT_VALUE are used.
+ * @hba: per adapter instance
+ * @option: Interrupt aggregation option
+ */
+static inline void
+ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
+{
+       switch (option) {
+       case INT_AGGR_RESET:
+               writel((INT_AGGR_ENABLE |
+                       INT_AGGR_COUNTER_AND_TIMER_RESET),
+                       (hba->mmio_base +
+                        REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+               break;
+       case INT_AGGR_CONFIG:
+               writel((INT_AGGR_ENABLE |
+                       INT_AGGR_PARAM_WRITE |
+                       INT_AGGR_COUNTER_THRESHOLD_VALUE |
+                       INT_AGGR_TIMEOUT_VALUE),
+                       (hba->mmio_base +
+                        REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+               break;
+       }
+}
+
+/**
+ * ufshcd_enable_run_stop_reg - Enable run-stop registers,
+ *                     When run-stop registers are set to 1, it indicates the
+ *                     host controller that it can process the requests
+ * @hba: per adapter instance
+ */
+static void ufshcd_enable_run_stop_reg(struct ufs_hba *hba)
+{
+       writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT,
+              (hba->mmio_base +
+               REG_UTP_TASK_REQ_LIST_RUN_STOP));
+       writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
+              (hba->mmio_base +
+               REG_UTP_TRANSFER_REQ_LIST_RUN_STOP));
+}
+
+/**
+ * ufshcd_hba_stop - Send controller to reset state
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_hba_stop(struct ufs_hba *hba)
+{
+       writel(CONTROLLER_DISABLE, (hba->mmio_base + REG_CONTROLLER_ENABLE));
+}
+
+/**
+ * ufshcd_hba_start - Start controller initialization sequence
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_hba_start(struct ufs_hba *hba)
+{
+       writel(CONTROLLER_ENABLE , (hba->mmio_base + REG_CONTROLLER_ENABLE));
+}
+
+/**
+ * ufshcd_is_hba_active - Get controller state
+ * @hba: per adapter instance
+ *
+ * Returns zero if controller is active, 1 otherwise
+ */
+static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
+{
+       return (readl(hba->mmio_base + REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
+}
+
+/**
+ * ufshcd_send_command - Send SCSI or device management commands
+ * @hba: per adapter instance
+ * @task_tag: Task tag of the command
+ */
+static inline
+void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
+{
+       __set_bit(task_tag, &hba->outstanding_reqs);
+       writel((1 << task_tag),
+              (hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL));
+}
+
+/**
+ * ufshcd_copy_sense_data - Copy sense data in case of check condition
+ * @lrb - pointer to local reference block
+ */
+static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
+{
+       int len;
+       if (lrbp->sense_buffer) {
+               len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
+               memcpy(lrbp->sense_buffer,
+                       lrbp->ucd_rsp_ptr->sense_data,
+                       min_t(int, len, SCSI_SENSE_BUFFERSIZE));
+       }
+}
+
+/**
+ * ufshcd_hba_capabilities - Read controller capabilities
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_hba_capabilities(struct ufs_hba *hba)
+{
+       hba->capabilities =
+               readl(hba->mmio_base + REG_CONTROLLER_CAPABILITIES);
+
+       /* nutrs and nutmrs are 0 based values */
+       hba->nutrs = (hba->capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1;
+       hba->nutmrs =
+       ((hba->capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS) >> 16) + 1;
+}
+
+/**
+ * ufshcd_send_uic_command - Send UIC commands to unipro layers
+ * @hba: per adapter instance
+ * @uic_command: UIC command
+ */
+static inline void
+ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
+{
+       /* Write Args */
+       writel(uic_cmnd->argument1,
+             (hba->mmio_base + REG_UIC_COMMAND_ARG_1));
+       writel(uic_cmnd->argument2,
+             (hba->mmio_base + REG_UIC_COMMAND_ARG_2));
+       writel(uic_cmnd->argument3,
+             (hba->mmio_base + REG_UIC_COMMAND_ARG_3));
+
+       /* Write UIC Cmd */
+       writel((uic_cmnd->command & COMMAND_OPCODE_MASK),
+              (hba->mmio_base + REG_UIC_COMMAND));
+}
+
+/**
+ * ufshcd_map_sg - Map scatter-gather list to prdt
+ * @lrbp - pointer to local reference block
+ *
+ * Returns 0 in case of success, non-zero value in case of failure
+ */
+static int ufshcd_map_sg(struct ufshcd_lrb *lrbp)
+{
+       struct ufshcd_sg_entry *prd_table;
+       struct scatterlist *sg;
+       struct scsi_cmnd *cmd;
+       int sg_segments;
+       int i;
+
+       cmd = lrbp->cmd;
+       sg_segments = scsi_dma_map(cmd);
+       if (sg_segments < 0)
+               return sg_segments;
+
+       if (sg_segments) {
+               lrbp->utr_descriptor_ptr->prd_table_length =
+                                       cpu_to_le16((u16) (sg_segments));
+
+               prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr;
+
+               scsi_for_each_sg(cmd, sg, sg_segments, i) {
+                       prd_table[i].size  =
+                               cpu_to_le32(((u32) sg_dma_len(sg))-1);
+                       prd_table[i].base_addr =
+                               cpu_to_le32(lower_32_bits(sg->dma_address));
+                       prd_table[i].upper_addr =
+                               cpu_to_le32(upper_32_bits(sg->dma_address));
+               }
+       } else {
+               lrbp->utr_descriptor_ptr->prd_table_length = 0;
+       }
+
+       return 0;
+}
+
+/**
+ * ufshcd_int_config - enable/disable interrupts
+ * @hba: per adapter instance
+ * @option: interrupt option
+ */
+static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
+{
+       switch (option) {
+       case UFSHCD_INT_ENABLE:
+               writel(hba->int_enable_mask,
+                     (hba->mmio_base + REG_INTERRUPT_ENABLE));
+               break;
+       case UFSHCD_INT_DISABLE:
+               if (hba->ufs_version == UFSHCI_VERSION_10)
+                       writel(INTERRUPT_DISABLE_MASK_10,
+                             (hba->mmio_base + REG_INTERRUPT_ENABLE));
+               else
+                       writel(INTERRUPT_DISABLE_MASK_11,
+                              (hba->mmio_base + REG_INTERRUPT_ENABLE));
+               break;
+       }
+}
+
+/**
+ * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
+ * @lrb - pointer to local reference block
+ */
+static void ufshcd_compose_upiu(struct ufshcd_lrb *lrbp)
+{
+       struct utp_transfer_req_desc *req_desc;
+       struct utp_upiu_cmd *ucd_cmd_ptr;
+       u32 data_direction;
+       u32 upiu_flags;
+
+       ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
+       req_desc = lrbp->utr_descriptor_ptr;
+
+       switch (lrbp->command_type) {
+       case UTP_CMD_TYPE_SCSI:
+               if (lrbp->cmd->sc_data_direction == DMA_FROM_DEVICE) {
+                       data_direction = UTP_DEVICE_TO_HOST;
+                       upiu_flags = UPIU_CMD_FLAGS_READ;
+               } else if (lrbp->cmd->sc_data_direction == DMA_TO_DEVICE) {
+                       data_direction = UTP_HOST_TO_DEVICE;
+                       upiu_flags = UPIU_CMD_FLAGS_WRITE;
+               } else {
+                       data_direction = UTP_NO_DATA_TRANSFER;
+                       upiu_flags = UPIU_CMD_FLAGS_NONE;
+               }
+
+               /* Transfer request descriptor header fields */
+               req_desc->header.dword_0 =
+                       cpu_to_le32(data_direction | UTP_SCSI_COMMAND);
+
+               /*
+                * assigning invalid value for command status. Controller
+                * updates OCS on command completion, with the command
+                * status
+                */
+               req_desc->header.dword_2 =
+                       cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+
+               /* command descriptor fields */
+               ucd_cmd_ptr->header.dword_0 =
+                       cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
+                                                     upiu_flags,
+                                                     lrbp->lun,
+                                                     lrbp->task_tag));
+               ucd_cmd_ptr->header.dword_1 =
+                       cpu_to_be32(
+                               UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
+                                                 0,
+                                                 0,
+                                                 0));
+
+               /* Total EHS length and Data segment length will be zero */
+               ucd_cmd_ptr->header.dword_2 = 0;
+
+               ucd_cmd_ptr->exp_data_transfer_len =
+                       cpu_to_be32(lrbp->cmd->transfersize);
+
+               memcpy(ucd_cmd_ptr->cdb,
+                      lrbp->cmd->cmnd,
+                      (min_t(unsigned short,
+                             lrbp->cmd->cmd_len,
+                             MAX_CDB_SIZE)));
+               break;
+       case UTP_CMD_TYPE_DEV_MANAGE:
+               /* For query function implementation */
+               break;
+       case UTP_CMD_TYPE_UFS:
+               /* For UFS native command implementation */
+               break;
+       } /* end of switch */
+}
+
+/**
+ * ufshcd_queuecommand - main entry point for SCSI requests
+ * @cmd: command from SCSI Midlayer
+ * @done: call back function
+ *
+ * Returns 0 for success, non-zero in case of failure
+ */
+static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
+{
+       struct ufshcd_lrb *lrbp;
+       struct ufs_hba *hba;
+       unsigned long flags;
+       int tag;
+       int err = 0;
+
+       hba = shost_priv(host);
+
+       tag = cmd->request->tag;
+
+       if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+               err = SCSI_MLQUEUE_HOST_BUSY;
+               goto out;
+       }
+
+       lrbp = &hba->lrb[tag];
+
+       lrbp->cmd = cmd;
+       lrbp->sense_bufflen = SCSI_SENSE_BUFFERSIZE;
+       lrbp->sense_buffer = cmd->sense_buffer;
+       lrbp->task_tag = tag;
+       lrbp->lun = cmd->device->lun;
+
+       lrbp->command_type = UTP_CMD_TYPE_SCSI;
+
+       /* form UPIU before issuing the command */
+       ufshcd_compose_upiu(lrbp);
+       err = ufshcd_map_sg(lrbp);
+       if (err)
+               goto out;
+
+       /* issue command to the controller */
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       ufshcd_send_command(hba, tag);
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+out:
+       return err;
+}
+
+/**
+ * ufshcd_memory_alloc - allocate memory for host memory space data structures
+ * @hba: per adapter instance
+ *
+ * 1. Allocate DMA memory for Command Descriptor array
+ *     Each command descriptor consist of Command UPIU, Response UPIU and PRDT
+ * 2. Allocate DMA memory for UTP Transfer Request Descriptor List (UTRDL).
+ * 3. Allocate DMA memory for UTP Task Management Request Descriptor List
+ *     (UTMRDL)
+ * 4. Allocate memory for local reference block(lrb).
+ *
+ * Returns 0 for success, non-zero in case of failure
+ */
+static int ufshcd_memory_alloc(struct ufs_hba *hba)
+{
+       size_t utmrdl_size, utrdl_size, ucdl_size;
+
+       /* Allocate memory for UTP command descriptors */
+       ucdl_size = (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs);
+       hba->ucdl_base_addr = dma_alloc_coherent(&hba->pdev->dev,
+                                                ucdl_size,
+                                                &hba->ucdl_dma_addr,
+                                                GFP_KERNEL);
+
+       /*
+        * UFSHCI requires UTP command descriptor to be 128 byte aligned.
+        * make sure hba->ucdl_dma_addr is aligned to PAGE_SIZE
+        * if hba->ucdl_dma_addr is aligned to PAGE_SIZE, then it will
+        * be aligned to 128 bytes as well
+        */
+       if (!hba->ucdl_base_addr ||
+           WARN_ON(hba->ucdl_dma_addr & (PAGE_SIZE - 1))) {
+               dev_err(&hba->pdev->dev,
+                       "Command Descriptor Memory allocation failed\n");
+               goto out;
+       }
+
+       /*
+        * Allocate memory for UTP Transfer descriptors
+        * UFSHCI requires 1024 byte alignment of UTRD
+        */
+       utrdl_size = (sizeof(struct utp_transfer_req_desc) * hba->nutrs);
+       hba->utrdl_base_addr = dma_alloc_coherent(&hba->pdev->dev,
+                                                 utrdl_size,
+                                                 &hba->utrdl_dma_addr,
+                                                 GFP_KERNEL);
+       if (!hba->utrdl_base_addr ||
+           WARN_ON(hba->utrdl_dma_addr & (PAGE_SIZE - 1))) {
+               dev_err(&hba->pdev->dev,
+                       "Transfer Descriptor Memory allocation failed\n");
+               goto out;
+       }
+
+       /*
+        * Allocate memory for UTP Task Management descriptors
+        * UFSHCI requires 1024 byte alignment of UTMRD
+        */
+       utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs;
+       hba->utmrdl_base_addr = dma_alloc_coherent(&hba->pdev->dev,
+                                                  utmrdl_size,
+                                                  &hba->utmrdl_dma_addr,
+                                                  GFP_KERNEL);
+       if (!hba->utmrdl_base_addr ||
+           WARN_ON(hba->utmrdl_dma_addr & (PAGE_SIZE - 1))) {
+               dev_err(&hba->pdev->dev,
+               "Task Management Descriptor Memory allocation failed\n");
+               goto out;
+       }
+
+       /* Allocate memory for local reference block */
+       hba->lrb = kcalloc(hba->nutrs, sizeof(struct ufshcd_lrb), GFP_KERNEL);
+       if (!hba->lrb) {
+               dev_err(&hba->pdev->dev, "LRB Memory allocation failed\n");
+               goto out;
+       }
+       return 0;
+out:
+       ufshcd_free_hba_memory(hba);
+       return -ENOMEM;
+}
+
+/**
+ * ufshcd_host_memory_configure - configure local reference block with
+ *                             memory offsets
+ * @hba: per adapter instance
+ *
+ * Configure Host memory space
+ * 1. Update Corresponding UTRD.UCDBA and UTRD.UCDBAU with UCD DMA
+ * address.
+ * 2. Update each UTRD with Response UPIU offset, Response UPIU length
+ * and PRDT offset.
+ * 3. Save the corresponding addresses of UTRD, UCD.CMD, UCD.RSP and UCD.PRDT
+ * into local reference block.
+ */
+static void ufshcd_host_memory_configure(struct ufs_hba *hba)
+{
+       struct utp_transfer_cmd_desc *cmd_descp;
+       struct utp_transfer_req_desc *utrdlp;
+       dma_addr_t cmd_desc_dma_addr;
+       dma_addr_t cmd_desc_element_addr;
+       u16 response_offset;
+       u16 prdt_offset;
+       int cmd_desc_size;
+       int i;
+
+       utrdlp = hba->utrdl_base_addr;
+       cmd_descp = hba->ucdl_base_addr;
+
+       response_offset =
+               offsetof(struct utp_transfer_cmd_desc, response_upiu);
+       prdt_offset =
+               offsetof(struct utp_transfer_cmd_desc, prd_table);
+
+       cmd_desc_size = sizeof(struct utp_transfer_cmd_desc);
+       cmd_desc_dma_addr = hba->ucdl_dma_addr;
+
+       for (i = 0; i < hba->nutrs; i++) {
+               /* Configure UTRD with command descriptor base address */
+               cmd_desc_element_addr =
+                               (cmd_desc_dma_addr + (cmd_desc_size * i));
+               utrdlp[i].command_desc_base_addr_lo =
+                               cpu_to_le32(lower_32_bits(cmd_desc_element_addr));
+               utrdlp[i].command_desc_base_addr_hi =
+                               cpu_to_le32(upper_32_bits(cmd_desc_element_addr));
+
+               /* Response upiu and prdt offset should be in double words */
+               utrdlp[i].response_upiu_offset =
+                               cpu_to_le16((response_offset >> 2));
+               utrdlp[i].prd_table_offset =
+                               cpu_to_le16((prdt_offset >> 2));
+               utrdlp[i].response_upiu_length =
+                               cpu_to_le16(ALIGNED_UPIU_SIZE);
+
+               hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
+               hba->lrb[i].ucd_cmd_ptr =
+                       (struct utp_upiu_cmd *)(cmd_descp + i);
+               hba->lrb[i].ucd_rsp_ptr =
+                       (struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
+               hba->lrb[i].ucd_prdt_ptr =
+                       (struct ufshcd_sg_entry *)cmd_descp[i].prd_table;
+       }
+}
+
+/**
+ * ufshcd_dme_link_startup - Notify Unipro to perform link startup
+ * @hba: per adapter instance
+ *
+ * UIC_CMD_DME_LINK_STARTUP command must be issued to Unipro layer,
+ * in order to initialize the Unipro link startup procedure.
+ * Once the Unipro links are up, the device connected to the controller
+ * is detected.
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_dme_link_startup(struct ufs_hba *hba)
+{
+       struct uic_command *uic_cmd;
+       unsigned long flags;
+
+       /* check if controller is ready to accept UIC commands */
+       if (((readl(hba->mmio_base + REG_CONTROLLER_STATUS)) &
+           UIC_COMMAND_READY) == 0x0) {
+               dev_err(&hba->pdev->dev,
+                       "Controller not ready"
+                       " to accept UIC commands\n");
+               return -EIO;
+       }
+
+       spin_lock_irqsave(hba->host->host_lock, flags);
+
+       /* form UIC command */
+       uic_cmd = &hba->active_uic_cmd;
+       uic_cmd->command = UIC_CMD_DME_LINK_STARTUP;
+       uic_cmd->argument1 = 0;
+       uic_cmd->argument2 = 0;
+       uic_cmd->argument3 = 0;
+
+       /* enable UIC related interrupts */
+       hba->int_enable_mask |= UIC_COMMAND_COMPL;
+       ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
+
+       /* sending UIC commands to controller */
+       ufshcd_send_uic_command(hba, uic_cmd);
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+       return 0;
+}
+
+/**
+ * ufshcd_make_hba_operational - Make UFS controller operational
+ * @hba: per adapter instance
+ *
+ * To bring UFS host controller to operational state,
+ * 1. Check if device is present
+ * 2. Configure run-stop-registers
+ * 3. Enable required interrupts
+ * 4. Configure interrupt aggregation
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_make_hba_operational(struct ufs_hba *hba)
+{
+       int err = 0;
+       u32 reg;
+
+       /* check if device present */
+       reg = readl((hba->mmio_base + REG_CONTROLLER_STATUS));
+       if (ufshcd_is_device_present(reg)) {
+               dev_err(&hba->pdev->dev, "cc: Device not present\n");
+               err = -ENXIO;
+               goto out;
+       }
+
+       /*
+        * UCRDY, UTMRLDY and UTRLRDY bits must be 1
+        * DEI, HEI bits must be 0
+        */
+       if (!(ufshcd_get_lists_status(reg))) {
+               ufshcd_enable_run_stop_reg(hba);
+       } else {
+               dev_err(&hba->pdev->dev,
+                       "Host controller not ready to process requests");
+               err = -EIO;
+               goto out;
+       }
+
+       /* Enable required interrupts */
+       hba->int_enable_mask |= (UTP_TRANSFER_REQ_COMPL |
+                                UIC_ERROR |
+                                UTP_TASK_REQ_COMPL |
+                                DEVICE_FATAL_ERROR |
+                                CONTROLLER_FATAL_ERROR |
+                                SYSTEM_BUS_FATAL_ERROR);
+       ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
+
+       /* Configure interrupt aggregation */
+       ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+
+       if (hba->ufshcd_state == UFSHCD_STATE_RESET)
+               scsi_unblock_requests(hba->host);
+
+       hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
+       scsi_scan_host(hba->host);
+out:
+       return err;
+}
+
+/**
+ * ufshcd_hba_enable - initialize the controller
+ * @hba: per adapter instance
+ *
+ * The controller resets itself and controller firmware initialization
+ * sequence kicks off. When controller is ready it will set
+ * the Host Controller Enable bit to 1.
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_hba_enable(struct ufs_hba *hba)
+{
+       int retry;
+
+       /*
+        * msleep of 1 and 5 used in this function might result in msleep(20),
+        * but it was necessary to send the UFS FPGA to reset mode during
+        * development and testing of this driver. msleep can be changed to
+        * mdelay and retry count can be reduced based on the controller.
+        */
+       if (!ufshcd_is_hba_active(hba)) {
+
+               /* change controller state to "reset state" */
+               ufshcd_hba_stop(hba);
+
+               /*
+                * This delay is based on the testing done with UFS host
+                * controller FPGA. The delay can be changed based on the
+                * host controller used.
+                */
+               msleep(5);
+       }
+
+       /* start controller initialization sequence */
+       ufshcd_hba_start(hba);
+
+       /*
+        * To initialize a UFS host controller HCE bit must be set to 1.
+        * During initialization the HCE bit value changes from 1->0->1.
+        * When the host controller completes initialization sequence
+        * it sets the value of HCE bit to 1. The same HCE bit is read back
+        * to check if the controller has completed initialization sequence.
+        * So without this delay the value HCE = 1, set in the previous
+        * instruction might be read back.
+        * This delay can be changed based on the controller.
+        */
+       msleep(1);
+
+       /* wait for the host controller to complete initialization */
+       retry = 10;
+       while (ufshcd_is_hba_active(hba)) {
+               if (retry) {
+                       retry--;
+               } else {
+                       dev_err(&hba->pdev->dev,
+                               "Controller enable failed\n");
+                       return -EIO;
+               }
+               msleep(5);
+       }
+       return 0;
+}
+
+/**
+ * ufshcd_initialize_hba - start the initialization process
+ * @hba: per adapter instance
+ *
+ * 1. Enable the controller via ufshcd_hba_enable.
+ * 2. Program the Transfer Request List Address with the starting address of
+ * UTRDL.
+ * 3. Program the Task Management Request List Address with starting address
+ * of UTMRDL.
+ *
+ * Returns 0 on success, non-zero value on failure.
+ */
+static int ufshcd_initialize_hba(struct ufs_hba *hba)
+{
+       if (ufshcd_hba_enable(hba))
+               return -EIO;
+
+       /* Configure UTRL and UTMRL base address registers */
+       writel(hba->utrdl_dma_addr,
+              (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_L));
+       writel(lower_32_bits(hba->utrdl_dma_addr),
+              (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_H));
+       writel(hba->utmrdl_dma_addr,
+              (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_L));
+       writel(upper_32_bits(hba->utmrdl_dma_addr),
+              (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_H));
+
+       /* Initialize unipro link startup procedure */
+       return ufshcd_dme_link_startup(hba);
+}
+
+/**
+ * ufshcd_do_reset - reset the host controller
+ * @hba: per adapter instance
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_do_reset(struct ufs_hba *hba)
+{
+       struct ufshcd_lrb *lrbp;
+       unsigned long flags;
+       int tag;
+
+       /* block commands from midlayer */
+       scsi_block_requests(hba->host);
+
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       hba->ufshcd_state = UFSHCD_STATE_RESET;
+
+       /* send controller to reset state */
+       ufshcd_hba_stop(hba);
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       /* abort outstanding commands */
+       for (tag = 0; tag < hba->nutrs; tag++) {
+               if (test_bit(tag, &hba->outstanding_reqs)) {
+                       lrbp = &hba->lrb[tag];
+                       scsi_dma_unmap(lrbp->cmd);
+                       lrbp->cmd->result = DID_RESET << 16;
+                       lrbp->cmd->scsi_done(lrbp->cmd);
+                       lrbp->cmd = NULL;
+               }
+       }
+
+       /* clear outstanding request/task bit maps */
+       hba->outstanding_reqs = 0;
+       hba->outstanding_tasks = 0;
+
+       /* start the initialization process */
+       if (ufshcd_initialize_hba(hba)) {
+               dev_err(&hba->pdev->dev,
+                       "Reset: Controller initialization failed\n");
+               return FAILED;
+       }
+       return SUCCESS;
+}
+
+/**
+ * ufshcd_slave_alloc - handle initial SCSI device configurations
+ * @sdev: pointer to SCSI device
+ *
+ * Returns success
+ */
+static int ufshcd_slave_alloc(struct scsi_device *sdev)
+{
+       struct ufs_hba *hba;
+
+       hba = shost_priv(sdev->host);
+       sdev->tagged_supported = 1;
+
+       /* Mode sense(6) is not supported by UFS, so use Mode sense(10) */
+       sdev->use_10_for_ms = 1;
+       scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
+
+       /*
+        * Inform SCSI Midlayer that the LUN queue depth is same as the
+        * controller queue depth. If a LUN queue depth is less than the
+        * controller queue depth and if the LUN reports
+        * SAM_STAT_TASK_SET_FULL, the LUN queue depth will be adjusted
+        * with scsi_adjust_queue_depth.
+        */
+       scsi_activate_tcq(sdev, hba->nutrs);
+       return 0;
+}
+
+/**
+ * ufshcd_slave_destroy - remove SCSI device configurations
+ * @sdev: pointer to SCSI device
+ */
+static void ufshcd_slave_destroy(struct scsi_device *sdev)
+{
+       struct ufs_hba *hba;
+
+       hba = shost_priv(sdev->host);
+       scsi_deactivate_tcq(sdev, hba->nutrs);
+}
+
+/**
+ * ufshcd_task_req_compl - handle task management request completion
+ * @hba: per adapter instance
+ * @index: index of the completed request
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
+{
+       struct utp_task_req_desc *task_req_descp;
+       struct utp_upiu_task_rsp *task_rsp_upiup;
+       unsigned long flags;
+       int ocs_value;
+       int task_result;
+
+       spin_lock_irqsave(hba->host->host_lock, flags);
+
+       /* Clear completed tasks from outstanding_tasks */
+       __clear_bit(index, &hba->outstanding_tasks);
+
+       task_req_descp = hba->utmrdl_base_addr;
+       ocs_value = ufshcd_get_tmr_ocs(&task_req_descp[index]);
+
+       if (ocs_value == OCS_SUCCESS) {
+               task_rsp_upiup = (struct utp_upiu_task_rsp *)
+                               task_req_descp[index].task_rsp_upiu;
+               task_result = be32_to_cpu(task_rsp_upiup->header.dword_1);
+               task_result = ((task_result & MASK_TASK_RESPONSE) >> 8);
+
+               if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL ||
+                   task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED)
+                       task_result = FAILED;
+       } else {
+               task_result = FAILED;
+               dev_err(&hba->pdev->dev,
+                       "trc: Invalid ocs = %x\n", ocs_value);
+       }
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+       return task_result;
+}
+
+/**
+ * ufshcd_adjust_lun_qdepth - Update LUN queue depth if device responds with
+ *                           SAM_STAT_TASK_SET_FULL SCSI command status.
+ * @cmd: pointer to SCSI command
+ */
+static void ufshcd_adjust_lun_qdepth(struct scsi_cmnd *cmd)
+{
+       struct ufs_hba *hba;
+       int i;
+       int lun_qdepth = 0;
+
+       hba = shost_priv(cmd->device->host);
+
+       /*
+        * LUN queue depth can be obtained by counting outstanding commands
+        * on the LUN.
+        */
+       for (i = 0; i < hba->nutrs; i++) {
+               if (test_bit(i, &hba->outstanding_reqs)) {
+
+                       /*
+                        * Check if the outstanding command belongs
+                        * to the LUN which reported SAM_STAT_TASK_SET_FULL.
+                        */
+                       if (cmd->device->lun == hba->lrb[i].lun)
+                               lun_qdepth++;
+               }
+       }
+
+       /*
+        * LUN queue depth will be total outstanding commands, except the
+        * command for which the LUN reported SAM_STAT_TASK_SET_FULL.
+        */
+       scsi_adjust_queue_depth(cmd->device, MSG_SIMPLE_TAG, lun_qdepth - 1);
+}
+
+/**
+ * ufshcd_scsi_cmd_status - Update SCSI command result based on SCSI status
+ * @lrb: pointer to local reference block of completed command
+ * @scsi_status: SCSI command status
+ *
+ * Returns value base on SCSI command status
+ */
+static inline int
+ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
+{
+       int result = 0;
+
+       switch (scsi_status) {
+       case SAM_STAT_GOOD:
+               result |= DID_OK << 16 |
+                         COMMAND_COMPLETE << 8 |
+                         SAM_STAT_GOOD;
+               break;
+       case SAM_STAT_CHECK_CONDITION:
+               result |= DID_OK << 16 |
+                         COMMAND_COMPLETE << 8 |
+                         SAM_STAT_CHECK_CONDITION;
+               ufshcd_copy_sense_data(lrbp);
+               break;
+       case SAM_STAT_BUSY:
+               result |= SAM_STAT_BUSY;
+               break;
+       case SAM_STAT_TASK_SET_FULL:
+
+               /*
+                * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
+                * depth needs to be adjusted to the exact number of
+                * outstanding commands the LUN can handle at any given time.
+                */
+               ufshcd_adjust_lun_qdepth(lrbp->cmd);
+               result |= SAM_STAT_TASK_SET_FULL;
+               break;
+       case SAM_STAT_TASK_ABORTED:
+               result |= SAM_STAT_TASK_ABORTED;
+               break;
+       default:
+               result |= DID_ERROR << 16;
+               break;
+       } /* end of switch */
+
+       return result;
+}
+
+/**
+ * ufshcd_transfer_rsp_status - Get overall status of the response
+ * @hba: per adapter instance
+ * @lrb: pointer to local reference block of completed command
+ *
+ * Returns result of the command to notify SCSI midlayer
+ */
+static inline int
+ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+       int result = 0;
+       int scsi_status;
+       int ocs;
+
+       /* overall command status of utrd */
+       ocs = ufshcd_get_tr_ocs(lrbp);
+
+       switch (ocs) {
+       case OCS_SUCCESS:
+
+               /* check if the returned transfer response is valid */
+               result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
+               if (result) {
+                       dev_err(&hba->pdev->dev,
+                               "Invalid response = %x\n", result);
+                       break;
+               }
+
+               /*
+                * get the response UPIU result to extract
+                * the SCSI command status
+                */
+               result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
+
+               /*
+                * get the result based on SCSI status response
+                * to notify the SCSI midlayer of the command status
+                */
+               scsi_status = result & MASK_SCSI_STATUS;
+               result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
+               break;
+       case OCS_ABORTED:
+               result |= DID_ABORT << 16;
+               break;
+       case OCS_INVALID_CMD_TABLE_ATTR:
+       case OCS_INVALID_PRDT_ATTR:
+       case OCS_MISMATCH_DATA_BUF_SIZE:
+       case OCS_MISMATCH_RESP_UPIU_SIZE:
+       case OCS_PEER_COMM_FAILURE:
+       case OCS_FATAL_ERROR:
+       default:
+               result |= DID_ERROR << 16;
+               dev_err(&hba->pdev->dev,
+               "OCS error from controller = %x\n", ocs);
+               break;
+       } /* end of switch */
+
+       return result;
+}
+
+/**
+ * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+{
+       struct ufshcd_lrb *lrb;
+       unsigned long completed_reqs;
+       u32 tr_doorbell;
+       int result;
+       int index;
+
+       lrb = hba->lrb;
+       tr_doorbell =
+               readl(hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL);
+       completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
+
+       for (index = 0; index < hba->nutrs; index++) {
+               if (test_bit(index, &completed_reqs)) {
+
+                       result = ufshcd_transfer_rsp_status(hba, &lrb[index]);
+
+                       if (lrb[index].cmd) {
+                               scsi_dma_unmap(lrb[index].cmd);
+                               lrb[index].cmd->result = result;
+                               lrb[index].cmd->scsi_done(lrb[index].cmd);
+
+                               /* Mark completed command as NULL in LRB */
+                               lrb[index].cmd = NULL;
+                       }
+               } /* end of if */
+       } /* end of for */
+
+       /* clear corresponding bits of completed commands */
+       hba->outstanding_reqs ^= completed_reqs;
+
+       /* Reset interrupt aggregation counters */
+       ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+}
+
+/**
+ * ufshcd_uic_cc_handler - handle UIC command completion
+ * @work: pointer to a work queue structure
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static void ufshcd_uic_cc_handler (struct work_struct *work)
+{
+       struct ufs_hba *hba;
+
+       hba = container_of(work, struct ufs_hba, uic_workq);
+
+       if ((hba->active_uic_cmd.command == UIC_CMD_DME_LINK_STARTUP) &&
+           !(ufshcd_get_uic_cmd_result(hba))) {
+
+               if (ufshcd_make_hba_operational(hba))
+                       dev_err(&hba->pdev->dev,
+                               "cc: hba not operational state\n");
+               return;
+       }
+}
+
+/**
+ * ufshcd_fatal_err_handler - handle fatal errors
+ * @hba: per adapter instance
+ */
+static void ufshcd_fatal_err_handler(struct work_struct *work)
+{
+       struct ufs_hba *hba;
+       hba = container_of(work, struct ufs_hba, feh_workq);
+
+       /* check if reset is already in progress */
+       if (hba->ufshcd_state != UFSHCD_STATE_RESET)
+               ufshcd_do_reset(hba);
+}
+
+/**
+ * ufshcd_err_handler - Check for fatal errors
+ * @work: pointer to a work queue structure
+ */
+static void ufshcd_err_handler(struct ufs_hba *hba)
+{
+       u32 reg;
+
+       if (hba->errors & INT_FATAL_ERRORS)
+               goto fatal_eh;
+
+       if (hba->errors & UIC_ERROR) {
+
+               reg = readl(hba->mmio_base +
+                           REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
+               if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
+                       goto fatal_eh;
+       }
+       return;
+fatal_eh:
+       hba->ufshcd_state = UFSHCD_STATE_ERROR;
+       schedule_work(&hba->feh_workq);
+}
+
+/**
+ * ufshcd_tmc_handler - handle task management function completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_tmc_handler(struct ufs_hba *hba)
+{
+       u32 tm_doorbell;
+
+       tm_doorbell = readl(hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL);
+       hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks;
+       wake_up_interruptible(&hba->ufshcd_tm_wait_queue);
+}
+
+/**
+ * ufshcd_sl_intr - Interrupt service routine
+ * @hba: per adapter instance
+ * @intr_status: contains interrupts generated by the controller
+ */
+static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
+{
+       hba->errors = UFSHCD_ERROR_MASK & intr_status;
+       if (hba->errors)
+               ufshcd_err_handler(hba);
+
+       if (intr_status & UIC_COMMAND_COMPL)
+               schedule_work(&hba->uic_workq);
+
+       if (intr_status & UTP_TASK_REQ_COMPL)
+               ufshcd_tmc_handler(hba);
+
+       if (intr_status & UTP_TRANSFER_REQ_COMPL)
+               ufshcd_transfer_req_compl(hba);
+}
+
+/**
+ * ufshcd_intr - Main interrupt service routine
+ * @irq: irq number
+ * @__hba: pointer to adapter instance
+ *
+ * Returns IRQ_HANDLED - If interrupt is valid
+ *             IRQ_NONE - If invalid interrupt
+ */
+static irqreturn_t ufshcd_intr(int irq, void *__hba)
+{
+       u32 intr_status;
+       irqreturn_t retval = IRQ_NONE;
+       struct ufs_hba *hba = __hba;
+
+       spin_lock(hba->host->host_lock);
+       intr_status = readl(hba->mmio_base + REG_INTERRUPT_STATUS);
+
+       if (intr_status) {
+               ufshcd_sl_intr(hba, intr_status);
+
+               /* If UFSHCI 1.0 then clear interrupt status register */
+               if (hba->ufs_version == UFSHCI_VERSION_10)
+                       writel(intr_status,
+                              (hba->mmio_base + REG_INTERRUPT_STATUS));
+               retval = IRQ_HANDLED;
+       }
+       spin_unlock(hba->host->host_lock);
+       return retval;
+}
+
+/**
+ * ufshcd_issue_tm_cmd - issues task management commands to controller
+ * @hba: per adapter instance
+ * @lrbp: pointer to local reference block
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int
+ufshcd_issue_tm_cmd(struct ufs_hba *hba,
+                   struct ufshcd_lrb *lrbp,
+                   u8 tm_function)
+{
+       struct utp_task_req_desc *task_req_descp;
+       struct utp_upiu_task_req *task_req_upiup;
+       struct Scsi_Host *host;
+       unsigned long flags;
+       int free_slot = 0;
+       int err;
+
+       host = hba->host;
+
+       spin_lock_irqsave(host->host_lock, flags);
+
+       /* If task management queue is full */
+       free_slot = ufshcd_get_tm_free_slot(hba);
+       if (free_slot >= hba->nutmrs) {
+               spin_unlock_irqrestore(host->host_lock, flags);
+               dev_err(&hba->pdev->dev, "Task management queue full\n");
+               err = FAILED;
+               goto out;
+       }
+
+       task_req_descp = hba->utmrdl_base_addr;
+       task_req_descp += free_slot;
+
+       /* Configure task request descriptor */
+       task_req_descp->header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD);
+       task_req_descp->header.dword_2 =
+                       cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+
+       /* Configure task request UPIU */
+       task_req_upiup =
+               (struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
+       task_req_upiup->header.dword_0 =
+               cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
+                                             lrbp->lun, lrbp->task_tag));
+       task_req_upiup->header.dword_1 =
+       cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
+
+       task_req_upiup->input_param1 = lrbp->lun;
+       task_req_upiup->input_param1 =
+               cpu_to_be32(task_req_upiup->input_param1);
+       task_req_upiup->input_param2 = lrbp->task_tag;
+       task_req_upiup->input_param2 =
+               cpu_to_be32(task_req_upiup->input_param2);
+
+       /* send command to the controller */
+       __set_bit(free_slot, &hba->outstanding_tasks);
+       writel((1 << free_slot),
+              (hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL));
+
+       spin_unlock_irqrestore(host->host_lock, flags);
+
+       /* wait until the task management command is completed */
+       err =
+       wait_event_interruptible_timeout(hba->ufshcd_tm_wait_queue,
+                                        (test_bit(free_slot,
+                                        &hba->tm_condition) != 0),
+                                        60 * HZ);
+       if (!err) {
+               dev_err(&hba->pdev->dev,
+                       "Task management command timed-out\n");
+               err = FAILED;
+               goto out;
+       }
+       clear_bit(free_slot, &hba->tm_condition);
+       return ufshcd_task_req_compl(hba, free_slot);
+out:
+       return err;
+}
+
+/**
+ * ufshcd_device_reset - reset device and abort all the pending commands
+ * @cmd: SCSI command pointer
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_device_reset(struct scsi_cmnd *cmd)
+{
+       struct Scsi_Host *host;
+       struct ufs_hba *hba;
+       unsigned int tag;
+       u32 pos;
+       int err;
+
+       host = cmd->device->host;
+       hba = shost_priv(host);
+       tag = cmd->request->tag;
+
+       err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_LOGICAL_RESET);
+       if (err)
+               goto out;
+
+       for (pos = 0; pos < hba->nutrs; pos++) {
+               if (test_bit(pos, &hba->outstanding_reqs) &&
+                   (hba->lrb[tag].lun == hba->lrb[pos].lun)) {
+
+                       /* clear the respective UTRLCLR register bit */
+                       ufshcd_utrl_clear(hba, pos);
+
+                       clear_bit(pos, &hba->outstanding_reqs);
+
+                       if (hba->lrb[pos].cmd) {
+                               scsi_dma_unmap(hba->lrb[pos].cmd);
+                               hba->lrb[pos].cmd->result =
+                                               DID_ABORT << 16;
+                               hba->lrb[pos].cmd->scsi_done(cmd);
+                               hba->lrb[pos].cmd = NULL;
+                       }
+               }
+       } /* end of for */
+out:
+       return err;
+}
+
+/**
+ * ufshcd_host_reset - Main reset function registered with scsi layer
+ * @cmd: SCSI command pointer
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_host_reset(struct scsi_cmnd *cmd)
+{
+       struct ufs_hba *hba;
+
+       hba = shost_priv(cmd->device->host);
+
+       if (hba->ufshcd_state == UFSHCD_STATE_RESET)
+               return SUCCESS;
+
+       return (ufshcd_do_reset(hba) == SUCCESS) ? SUCCESS : FAILED;
+}
+
+/**
+ * ufshcd_abort - abort a specific command
+ * @cmd: SCSI command pointer
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_abort(struct scsi_cmnd *cmd)
+{
+       struct Scsi_Host *host;
+       struct ufs_hba *hba;
+       unsigned long flags;
+       unsigned int tag;
+       int err;
+
+       host = cmd->device->host;
+       hba = shost_priv(host);
+       tag = cmd->request->tag;
+
+       spin_lock_irqsave(host->host_lock, flags);
+
+       /* check if command is still pending */
+       if (!(test_bit(tag, &hba->outstanding_reqs))) {
+               err = FAILED;
+               spin_unlock_irqrestore(host->host_lock, flags);
+               goto out;
+       }
+       spin_unlock_irqrestore(host->host_lock, flags);
+
+       err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK);
+       if (err)
+               goto out;
+
+       scsi_dma_unmap(cmd);
+
+       spin_lock_irqsave(host->host_lock, flags);
+
+       /* clear the respective UTRLCLR register bit */
+       ufshcd_utrl_clear(hba, tag);
+
+       __clear_bit(tag, &hba->outstanding_reqs);
+       hba->lrb[tag].cmd = NULL;
+       spin_unlock_irqrestore(host->host_lock, flags);
+out:
+       return err;
+}
+
+static struct scsi_host_template ufshcd_driver_template = {
+       .module                 = THIS_MODULE,
+       .name                   = UFSHCD,
+       .proc_name              = UFSHCD,
+       .queuecommand           = ufshcd_queuecommand,
+       .slave_alloc            = ufshcd_slave_alloc,
+       .slave_destroy          = ufshcd_slave_destroy,
+       .eh_abort_handler       = ufshcd_abort,
+       .eh_device_reset_handler = ufshcd_device_reset,
+       .eh_host_reset_handler  = ufshcd_host_reset,
+       .this_id                = -1,
+       .sg_tablesize           = SG_ALL,
+       .cmd_per_lun            = UFSHCD_CMD_PER_LUN,
+       .can_queue              = UFSHCD_CAN_QUEUE,
+};
+
+/**
+ * ufshcd_shutdown - main function to put the controller in reset state
+ * @pdev: pointer to PCI device handle
+ */
+static void ufshcd_shutdown(struct pci_dev *pdev)
+{
+       ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
+}
+
+#ifdef CONFIG_PM
+/**
+ * ufshcd_suspend - suspend power management function
+ * @pdev: pointer to PCI device handle
+ * @state: power state
+ *
+ * Returns -ENOSYS
+ */
+static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       /*
+        * TODO:
+        * 1. Block SCSI requests from SCSI midlayer
+        * 2. Change the internal driver state to non operational
+        * 3. Set UTRLRSR and UTMRLRSR bits to zero
+        * 4. Wait until outstanding commands are completed
+        * 5. Set HCE to zero to send the UFS host controller to reset state
+        */
+
+       return -ENOSYS;
+}
+
+/**
+ * ufshcd_resume - resume power management function
+ * @pdev: pointer to PCI device handle
+ *
+ * Returns -ENOSYS
+ */
+static int ufshcd_resume(struct pci_dev *pdev)
+{
+       /*
+        * TODO:
+        * 1. Set HCE to 1, to start the UFS host controller
+        * initialization process
+        * 2. Set UTRLRSR and UTMRLRSR bits to 1
+        * 3. Change the internal driver state to operational
+        * 4. Unblock SCSI requests from SCSI midlayer
+        */
+
+       return -ENOSYS;
+}
+#endif /* CONFIG_PM */
+
+/**
+ * ufshcd_hba_free - free allocated memory for
+ *                     host memory space data structures
+ * @hba: per adapter instance
+ */
+static void ufshcd_hba_free(struct ufs_hba *hba)
+{
+       iounmap(hba->mmio_base);
+       ufshcd_free_hba_memory(hba);
+       pci_release_regions(hba->pdev);
+}
+
+/**
+ * ufshcd_remove - de-allocate PCI/SCSI host and host memory space
+ *             data structure memory
+ * @pdev - pointer to PCI handle
+ */
+static void ufshcd_remove(struct pci_dev *pdev)
+{
+       struct ufs_hba *hba = pci_get_drvdata(pdev);
+
+       /* disable interrupts */
+       ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
+       free_irq(pdev->irq, hba);
+
+       ufshcd_hba_stop(hba);
+       ufshcd_hba_free(hba);
+
+       scsi_remove_host(hba->host);
+       scsi_host_put(hba->host);
+       pci_set_drvdata(pdev, NULL);
+       pci_clear_master(pdev);
+       pci_disable_device(pdev);
+}
+
+/**
+ * ufshcd_set_dma_mask - Set dma mask based on the controller
+ *                      addressing capability
+ * @pdev: PCI device structure
+ *
+ * Returns 0 for success, non-zero for failure
+ */
+static int ufshcd_set_dma_mask(struct ufs_hba *hba)
+{
+       int err;
+       u64 dma_mask;
+
+       /*
+        * If controller supports 64 bit addressing mode, then set the DMA
+        * mask to 64-bit, else set the DMA mask to 32-bit
+        */
+       if (hba->capabilities & MASK_64_ADDRESSING_SUPPORT)
+               dma_mask = DMA_BIT_MASK(64);
+       else
+               dma_mask = DMA_BIT_MASK(32);
+
+       err = pci_set_dma_mask(hba->pdev, dma_mask);
+       if (err)
+               return err;
+
+       err = pci_set_consistent_dma_mask(hba->pdev, dma_mask);
+
+       return err;
+}
+
+/**
+ * ufshcd_probe - probe routine of the driver
+ * @pdev: pointer to PCI device handle
+ * @id: PCI device id
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int __devinit
+ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+       struct Scsi_Host *host;
+       struct ufs_hba *hba;
+       int err;
+
+       err = pci_enable_device(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "pci_enable_device failed\n");
+               goto out_error;
+       }
+
+       pci_set_master(pdev);
+
+       host = scsi_host_alloc(&ufshcd_driver_template,
+                               sizeof(struct ufs_hba));
+       if (!host) {
+               dev_err(&pdev->dev, "scsi_host_alloc failed\n");
+               err = -ENOMEM;
+               goto out_disable;
+       }
+       hba = shost_priv(host);
+
+       err = pci_request_regions(pdev, UFSHCD);
+       if (err < 0) {
+               dev_err(&pdev->dev, "request regions failed\n");
+               goto out_disable;
+       }
+
+       hba->mmio_base = pci_ioremap_bar(pdev, 0);
+       if (!hba->mmio_base) {
+               dev_err(&pdev->dev, "memory map failed\n");
+               err = -ENOMEM;
+               goto out_release_regions;
+       }
+
+       hba->host = host;
+       hba->pdev = pdev;
+
+       /* Read capabilities registers */
+       ufshcd_hba_capabilities(hba);
+
+       /* Get UFS version supported by the controller */
+       hba->ufs_version = ufshcd_get_ufs_version(hba);
+
+       err = ufshcd_set_dma_mask(hba);
+       if (err) {
+               dev_err(&pdev->dev, "set dma mask failed\n");
+               goto out_iounmap;
+       }
+
+       /* Allocate memory for host memory space */
+       err = ufshcd_memory_alloc(hba);
+       if (err) {
+               dev_err(&pdev->dev, "Memory allocation failed\n");
+               goto out_iounmap;
+       }
+
+       /* Configure LRB */
+       ufshcd_host_memory_configure(hba);
+
+       host->can_queue = hba->nutrs;
+       host->cmd_per_lun = hba->nutrs;
+       host->max_id = UFSHCD_MAX_ID;
+       host->max_lun = UFSHCD_MAX_LUNS;
+       host->max_channel = UFSHCD_MAX_CHANNEL;
+       host->unique_id = host->host_no;
+       host->max_cmd_len = MAX_CDB_SIZE;
+
+       /* Initailize wait queue for task management */
+       init_waitqueue_head(&hba->ufshcd_tm_wait_queue);
+
+       /* Initialize work queues */
+       INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
+       INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
+
+       /* IRQ registration */
+       err = request_irq(pdev->irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
+       if (err) {
+               dev_err(&pdev->dev, "request irq failed\n");
+               goto out_lrb_free;
+       }
+
+       /* Enable SCSI tag mapping */
+       err = scsi_init_shared_tag_map(host, host->can_queue);
+       if (err) {
+               dev_err(&pdev->dev, "init shared queue failed\n");
+               goto out_free_irq;
+       }
+
+       pci_set_drvdata(pdev, hba);
+
+       err = scsi_add_host(host, &pdev->dev);
+       if (err) {
+               dev_err(&pdev->dev, "scsi_add_host failed\n");
+               goto out_free_irq;
+       }
+
+       /* Initialization routine */
+       err = ufshcd_initialize_hba(hba);
+       if (err) {
+               dev_err(&pdev->dev, "Initialization failed\n");
+               goto out_free_irq;
+       }
+
+       return 0;
+
+out_free_irq:
+       free_irq(pdev->irq, hba);
+out_lrb_free:
+       ufshcd_free_hba_memory(hba);
+out_iounmap:
+       iounmap(hba->mmio_base);
+out_release_regions:
+       pci_release_regions(pdev);
+out_disable:
+       scsi_host_put(host);
+       pci_clear_master(pdev);
+       pci_disable_device(pdev);
+out_error:
+       return err;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
+       { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { }     /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl);
+
+static struct pci_driver ufshcd_pci_driver = {
+       .name = UFSHCD,
+       .id_table = ufshcd_pci_tbl,
+       .probe = ufshcd_probe,
+       .remove = __devexit_p(ufshcd_remove),
+       .shutdown = ufshcd_shutdown,
+#ifdef CONFIG_PM
+       .suspend = ufshcd_suspend,
+       .resume = ufshcd_resume,
+#endif
+};
+
+/**
+ * ufshcd_init - Driver registration routine
+ */
+static int __init ufshcd_init(void)
+{
+       return pci_register_driver(&ufshcd_pci_driver);
+}
+module_init(ufshcd_init);
+
+/**
+ * ufshcd_exit - Driver exit clean-up routine
+ */
+static void __exit ufshcd_exit(void)
+{
+       pci_unregister_driver(&ufshcd_pci_driver);
+}
+module_exit(ufshcd_exit);
+
+
+MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>, "
+             "Vinayak Holikatti <h.vinayak@samsung.com>");
+MODULE_DESCRIPTION("Generic UFS host controller driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(UFSHCD_DRIVER_VERSION);
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
new file mode 100644 (file)
index 0000000..6e3510f
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Universal Flash Storage Host controller driver
+ *
+ * This code is based on drivers/scsi/ufs/ufshci.h
+ * Copyright (C) 2011-2012 Samsung India Software Operations
+ *
+ * Santosh Yaraganavi <santosh.sy@samsung.com>
+ * Vinayak Holikatti <h.vinayak@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.
+ *
+ * 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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * 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 Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ */
+
+#ifndef _UFSHCI_H
+#define _UFSHCI_H
+
+enum {
+       TASK_REQ_UPIU_SIZE_DWORDS       = 8,
+       TASK_RSP_UPIU_SIZE_DWORDS       = 8,
+       ALIGNED_UPIU_SIZE               = 128,
+};
+
+/* UFSHCI Registers */
+enum {
+       REG_CONTROLLER_CAPABILITIES             = 0x00,
+       REG_UFS_VERSION                         = 0x08,
+       REG_CONTROLLER_DEV_ID                   = 0x10,
+       REG_CONTROLLER_PROD_ID                  = 0x14,
+       REG_INTERRUPT_STATUS                    = 0x20,
+       REG_INTERRUPT_ENABLE                    = 0x24,
+       REG_CONTROLLER_STATUS                   = 0x30,
+       REG_CONTROLLER_ENABLE                   = 0x34,
+       REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER    = 0x38,
+       REG_UIC_ERROR_CODE_DATA_LINK_LAYER      = 0x3C,
+       REG_UIC_ERROR_CODE_NETWORK_LAYER        = 0x40,
+       REG_UIC_ERROR_CODE_TRANSPORT_LAYER      = 0x44,
+       REG_UIC_ERROR_CODE_DME                  = 0x48,
+       REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL    = 0x4C,
+       REG_UTP_TRANSFER_REQ_LIST_BASE_L        = 0x50,
+       REG_UTP_TRANSFER_REQ_LIST_BASE_H        = 0x54,
+       REG_UTP_TRANSFER_REQ_DOOR_BELL          = 0x58,
+       REG_UTP_TRANSFER_REQ_LIST_CLEAR         = 0x5C,
+       REG_UTP_TRANSFER_REQ_LIST_RUN_STOP      = 0x60,
+       REG_UTP_TASK_REQ_LIST_BASE_L            = 0x70,
+       REG_UTP_TASK_REQ_LIST_BASE_H            = 0x74,
+       REG_UTP_TASK_REQ_DOOR_BELL              = 0x78,
+       REG_UTP_TASK_REQ_LIST_CLEAR             = 0x7C,
+       REG_UTP_TASK_REQ_LIST_RUN_STOP          = 0x80,
+       REG_UIC_COMMAND                         = 0x90,
+       REG_UIC_COMMAND_ARG_1                   = 0x94,
+       REG_UIC_COMMAND_ARG_2                   = 0x98,
+       REG_UIC_COMMAND_ARG_3                   = 0x9C,
+};
+
+/* Controller capability masks */
+enum {
+       MASK_TRANSFER_REQUESTS_SLOTS            = 0x0000001F,
+       MASK_TASK_MANAGEMENT_REQUEST_SLOTS      = 0x00070000,
+       MASK_64_ADDRESSING_SUPPORT              = 0x01000000,
+       MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
+       MASK_UIC_DME_TEST_MODE_SUPPORT          = 0x04000000,
+};
+
+/* UFS Version 08h */
+#define MINOR_VERSION_NUM_MASK         UFS_MASK(0xFFFF, 0)
+#define MAJOR_VERSION_NUM_MASK         UFS_MASK(0xFFFF, 16)
+
+/* Controller UFSHCI version */
+enum {
+       UFSHCI_VERSION_10 = 0x00010000,
+       UFSHCI_VERSION_11 = 0x00010100,
+};
+
+/*
+ * HCDDID - Host Controller Identification Descriptor
+ *       - Device ID and Device Class 10h
+ */
+#define DEVICE_CLASS   UFS_MASK(0xFFFF, 0)
+#define DEVICE_ID      UFS_MASK(0xFF, 24)
+
+/*
+ * HCPMID - Host Controller Identification Descriptor
+ *       - Product/Manufacturer ID  14h
+ */
+#define MANUFACTURE_ID_MASK    UFS_MASK(0xFFFF, 0)
+#define PRODUCT_ID_MASK                UFS_MASK(0xFFFF, 16)
+
+#define UFS_BIT(x)     (1L << (x))
+
+#define UTP_TRANSFER_REQ_COMPL                 UFS_BIT(0)
+#define UIC_DME_END_PT_RESET                   UFS_BIT(1)
+#define UIC_ERROR                              UFS_BIT(2)
+#define UIC_TEST_MODE                          UFS_BIT(3)
+#define UIC_POWER_MODE                         UFS_BIT(4)
+#define UIC_HIBERNATE_EXIT                     UFS_BIT(5)
+#define UIC_HIBERNATE_ENTER                    UFS_BIT(6)
+#define UIC_LINK_LOST                          UFS_BIT(7)
+#define UIC_LINK_STARTUP                       UFS_BIT(8)
+#define UTP_TASK_REQ_COMPL                     UFS_BIT(9)
+#define UIC_COMMAND_COMPL                      UFS_BIT(10)
+#define DEVICE_FATAL_ERROR                     UFS_BIT(11)
+#define CONTROLLER_FATAL_ERROR                 UFS_BIT(16)
+#define SYSTEM_BUS_FATAL_ERROR                 UFS_BIT(17)
+
+#define UFSHCD_ERROR_MASK      (UIC_ERROR |\
+                               DEVICE_FATAL_ERROR |\
+                               CONTROLLER_FATAL_ERROR |\
+                               SYSTEM_BUS_FATAL_ERROR)
+
+#define INT_FATAL_ERRORS       (DEVICE_FATAL_ERROR |\
+                               CONTROLLER_FATAL_ERROR |\
+                               SYSTEM_BUS_FATAL_ERROR)
+
+/* HCS - Host Controller Status 30h */
+#define DEVICE_PRESENT                         UFS_BIT(0)
+#define UTP_TRANSFER_REQ_LIST_READY            UFS_BIT(1)
+#define UTP_TASK_REQ_LIST_READY                        UFS_BIT(2)
+#define UIC_COMMAND_READY                      UFS_BIT(3)
+#define HOST_ERROR_INDICATOR                   UFS_BIT(4)
+#define DEVICE_ERROR_INDICATOR                 UFS_BIT(5)
+#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK  UFS_MASK(0x7, 8)
+
+/* HCE - Host Controller Enable 34h */
+#define CONTROLLER_ENABLE      UFS_BIT(0)
+#define CONTROLLER_DISABLE     0x0
+
+/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */
+#define UIC_PHY_ADAPTER_LAYER_ERROR                    UFS_BIT(31)
+#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK          0x1F
+
+/* UECDL - Host UIC Error Code Data Link Layer 3Ch */
+#define UIC_DATA_LINK_LAYER_ERROR              UFS_BIT(31)
+#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK    0x7FFF
+#define UIC_DATA_LINK_LAYER_ERROR_PA_INIT      0x2000
+
+/* UECN - Host UIC Error Code Network Layer 40h */
+#define UIC_NETWORK_LAYER_ERROR                        UFS_BIT(31)
+#define UIC_NETWORK_LAYER_ERROR_CODE_MASK      0x7
+
+/* UECT - Host UIC Error Code Transport Layer 44h */
+#define UIC_TRANSPORT_LAYER_ERROR              UFS_BIT(31)
+#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK    0x7F
+
+/* UECDME - Host UIC Error Code DME 48h */
+#define UIC_DME_ERROR                  UFS_BIT(31)
+#define UIC_DME_ERROR_CODE_MASK                0x1
+
+#define INT_AGGR_TIMEOUT_VAL_MASK              0xFF
+#define INT_AGGR_COUNTER_THRESHOLD_MASK                UFS_MASK(0x1F, 8)
+#define INT_AGGR_COUNTER_AND_TIMER_RESET       UFS_BIT(16)
+#define INT_AGGR_STATUS_BIT                    UFS_BIT(20)
+#define INT_AGGR_PARAM_WRITE                   UFS_BIT(24)
+#define INT_AGGR_ENABLE                                UFS_BIT(31)
+
+/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */
+#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT     UFS_BIT(0)
+
+/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */
+#define UTP_TASK_REQ_LIST_RUN_STOP_BIT         UFS_BIT(0)
+
+/* UICCMD - UIC Command */
+#define COMMAND_OPCODE_MASK            0xFF
+#define GEN_SELECTOR_INDEX_MASK                0xFFFF
+
+#define MIB_ATTRIBUTE_MASK             UFS_MASK(0xFFFF, 16)
+#define RESET_LEVEL                    0xFF
+
+#define ATTR_SET_TYPE_MASK             UFS_MASK(0xFF, 16)
+#define CONFIG_RESULT_CODE_MASK                0xFF
+#define GENERIC_ERROR_CODE_MASK                0xFF
+
+/* UIC Commands */
+enum {
+       UIC_CMD_DME_GET                 = 0x01,
+       UIC_CMD_DME_SET                 = 0x02,
+       UIC_CMD_DME_PEER_GET            = 0x03,
+       UIC_CMD_DME_PEER_SET            = 0x04,
+       UIC_CMD_DME_POWERON             = 0x10,
+       UIC_CMD_DME_POWEROFF            = 0x11,
+       UIC_CMD_DME_ENABLE              = 0x12,
+       UIC_CMD_DME_RESET               = 0x14,
+       UIC_CMD_DME_END_PT_RST          = 0x15,
+       UIC_CMD_DME_LINK_STARTUP        = 0x16,
+       UIC_CMD_DME_HIBER_ENTER         = 0x17,
+       UIC_CMD_DME_HIBER_EXIT          = 0x18,
+       UIC_CMD_DME_TEST_MODE           = 0x1A,
+};
+
+/* UIC Config result code / Generic error code */
+enum {
+       UIC_CMD_RESULT_SUCCESS                  = 0x00,
+       UIC_CMD_RESULT_INVALID_ATTR             = 0x01,
+       UIC_CMD_RESULT_FAILURE                  = 0x01,
+       UIC_CMD_RESULT_INVALID_ATTR_VALUE       = 0x02,
+       UIC_CMD_RESULT_READ_ONLY_ATTR           = 0x03,
+       UIC_CMD_RESULT_WRITE_ONLY_ATTR          = 0x04,
+       UIC_CMD_RESULT_BAD_INDEX                = 0x05,
+       UIC_CMD_RESULT_LOCKED_ATTR              = 0x06,
+       UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX   = 0x07,
+       UIC_CMD_RESULT_PEER_COMM_FAILURE        = 0x08,
+       UIC_CMD_RESULT_BUSY                     = 0x09,
+       UIC_CMD_RESULT_DME_FAILURE              = 0x0A,
+};
+
+#define MASK_UIC_COMMAND_RESULT                        0xFF
+
+#define INT_AGGR_COUNTER_THRESHOLD_VALUE       (0x1F << 8)
+#define INT_AGGR_TIMEOUT_VALUE                 (0x02)
+
+/* Interrupt disable masks */
+enum {
+       /* Interrupt disable mask for UFSHCI v1.0 */
+       INTERRUPT_DISABLE_MASK_10       = 0xFFFF,
+
+       /* Interrupt disable mask for UFSHCI v1.1 */
+       INTERRUPT_DISABLE_MASK_11       = 0x0,
+};
+
+/*
+ * Request Descriptor Definitions
+ */
+
+/* Transfer request command type */
+enum {
+       UTP_CMD_TYPE_SCSI               = 0x0,
+       UTP_CMD_TYPE_UFS                = 0x1,
+       UTP_CMD_TYPE_DEV_MANAGE         = 0x2,
+};
+
+enum {
+       UTP_SCSI_COMMAND                = 0x00000000,
+       UTP_NATIVE_UFS_COMMAND          = 0x10000000,
+       UTP_DEVICE_MANAGEMENT_FUNCTION  = 0x20000000,
+       UTP_REQ_DESC_INT_CMD            = 0x01000000,
+};
+
+/* UTP Transfer Request Data Direction (DD) */
+enum {
+       UTP_NO_DATA_TRANSFER    = 0x00000000,
+       UTP_HOST_TO_DEVICE      = 0x02000000,
+       UTP_DEVICE_TO_HOST      = 0x04000000,
+};
+
+/* Overall command status values */
+enum {
+       OCS_SUCCESS                     = 0x0,
+       OCS_INVALID_CMD_TABLE_ATTR      = 0x1,
+       OCS_INVALID_PRDT_ATTR           = 0x2,
+       OCS_MISMATCH_DATA_BUF_SIZE      = 0x3,
+       OCS_MISMATCH_RESP_UPIU_SIZE     = 0x4,
+       OCS_PEER_COMM_FAILURE           = 0x5,
+       OCS_ABORTED                     = 0x6,
+       OCS_FATAL_ERROR                 = 0x7,
+       OCS_INVALID_COMMAND_STATUS      = 0x0F,
+       MASK_OCS                        = 0x0F,
+};
+
+/**
+ * struct ufshcd_sg_entry - UFSHCI PRD Entry
+ * @base_addr: Lower 32bit physical address DW-0
+ * @upper_addr: Upper 32bit physical address DW-1
+ * @reserved: Reserved for future use DW-2
+ * @size: size of physical segment DW-3
+ */
+struct ufshcd_sg_entry {
+       u32    base_addr;
+       u32    upper_addr;
+       u32    reserved;
+       u32    size;
+};
+
+/**
+ * struct utp_transfer_cmd_desc - UFS Command Descriptor structure
+ * @command_upiu: Command UPIU Frame address
+ * @response_upiu: Response UPIU Frame address
+ * @prd_table: Physical Region Descriptor
+ */
+struct utp_transfer_cmd_desc {
+       u8 command_upiu[ALIGNED_UPIU_SIZE];
+       u8 response_upiu[ALIGNED_UPIU_SIZE];
+       struct ufshcd_sg_entry    prd_table[SG_ALL];
+};
+
+/**
+ * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
+ * @dword0: Descriptor Header DW0
+ * @dword1: Descriptor Header DW1
+ * @dword2: Descriptor Header DW2
+ * @dword3: Descriptor Header DW3
+ */
+struct request_desc_header {
+       u32 dword_0;
+       u32 dword_1;
+       u32 dword_2;
+       u32 dword_3;
+};
+
+/**
+ * struct utp_transfer_req_desc - UTRD structure
+ * @header: UTRD header DW-0 to DW-3
+ * @command_desc_base_addr_lo: UCD base address low DW-4
+ * @command_desc_base_addr_hi: UCD base address high DW-5
+ * @response_upiu_length: response UPIU length DW-6
+ * @response_upiu_offset: response UPIU offset DW-6
+ * @prd_table_length: Physical region descriptor length DW-7
+ * @prd_table_offset: Physical region descriptor offset DW-7
+ */
+struct utp_transfer_req_desc {
+
+       /* DW 0-3 */
+       struct request_desc_header header;
+
+       /* DW 4-5*/
+       u32  command_desc_base_addr_lo;
+       u32  command_desc_base_addr_hi;
+
+       /* DW 6 */
+       u16  response_upiu_length;
+       u16  response_upiu_offset;
+
+       /* DW 7 */
+       u16  prd_table_length;
+       u16  prd_table_offset;
+};
+
+/**
+ * struct utp_task_req_desc - UTMRD structure
+ * @header: UTMRD header DW-0 to DW-3
+ * @task_req_upiu: Pointer to task request UPIU DW-4 to DW-11
+ * @task_rsp_upiu: Pointer to task response UPIU DW12 to DW-19
+ */
+struct utp_task_req_desc {
+
+       /* DW 0-3 */
+       struct request_desc_header header;
+
+       /* DW 4-11 */
+       u32 task_req_upiu[TASK_REQ_UPIU_SIZE_DWORDS];
+
+       /* DW 12-19 */
+       u32 task_rsp_upiu[TASK_RSP_UPIU_SIZE_DWORDS];
+};
+
+#endif /* End of Header */
index 7264116185d590eba8ed6154552c0f17b3ad109a..4411d42244011ffa2d562db19d7a7e654f2e7fa4 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Maintained by: Alok N Kataria <akataria@vmware.com>
+ * Maintained by: Arvind Kumar <arvindkumar@vmware.com>
  *
  */
 
@@ -1178,11 +1178,67 @@ static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
        return 0;
 }
 
+/*
+ * Query the device, fetch the config info and return the
+ * maximum number of targets on the adapter. In case of
+ * failure due to any reason return default i.e. 16.
+ */
+static u32 pvscsi_get_max_targets(struct pvscsi_adapter *adapter)
+{
+       struct PVSCSICmdDescConfigCmd cmd;
+       struct PVSCSIConfigPageHeader *header;
+       struct device *dev;
+       dma_addr_t configPagePA;
+       void *config_page;
+       u32 numPhys = 16;
+
+       dev = pvscsi_dev(adapter);
+       config_page = pci_alloc_consistent(adapter->dev, PAGE_SIZE,
+                                          &configPagePA);
+       if (!config_page) {
+               dev_warn(dev, "vmw_pvscsi: failed to allocate memory for config page\n");
+               goto exit;
+       }
+       BUG_ON(configPagePA & ~PAGE_MASK);
+
+       /* Fetch config info from the device. */
+       cmd.configPageAddress = ((u64)PVSCSI_CONFIG_CONTROLLER_ADDRESS) << 32;
+       cmd.configPageNum = PVSCSI_CONFIG_PAGE_CONTROLLER;
+       cmd.cmpAddr = configPagePA;
+       cmd._pad = 0;
+
+       /*
+        * Mark the completion page header with error values. If the device
+        * completes the command successfully, it sets the status values to
+        * indicate success.
+        */
+       header = config_page;
+       memset(header, 0, sizeof *header);
+       header->hostStatus = BTSTAT_INVPARAM;
+       header->scsiStatus = SDSTAT_CHECK;
+
+       pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_CONFIG, &cmd, sizeof cmd);
+
+       if (header->hostStatus == BTSTAT_SUCCESS &&
+           header->scsiStatus == SDSTAT_GOOD) {
+               struct PVSCSIConfigPageController *config;
+
+               config = config_page;
+               numPhys = config->numPhys;
+       } else
+               dev_warn(dev, "vmw_pvscsi: PVSCSI_CMD_CONFIG failed. hostStatus = 0x%x, scsiStatus = 0x%x\n",
+                        header->hostStatus, header->scsiStatus);
+       pci_free_consistent(adapter->dev, PAGE_SIZE, config_page, configPagePA);
+exit:
+       return numPhys;
+}
+
 static int __devinit pvscsi_probe(struct pci_dev *pdev,
                                  const struct pci_device_id *id)
 {
        struct pvscsi_adapter *adapter;
        struct Scsi_Host *host;
+       struct device *dev;
        unsigned int i;
        unsigned long flags = 0;
        int error;
@@ -1271,6 +1327,13 @@ static int __devinit pvscsi_probe(struct pci_dev *pdev,
                goto out_release_resources;
        }
 
+       /*
+        * Ask the device for max number of targets.
+        */
+       host->max_id = pvscsi_get_max_targets(adapter);
+       dev = pvscsi_dev(adapter);
+       dev_info(dev, "vmw_pvscsi: host->max_id: %u\n", host->max_id);
+
        /*
         * From this point on we should reset the adapter if anything goes
         * wrong.
index 62e36e75715e310a98ca219cb06f24fe3302b5d5..3546e8662e30af8f85e24f896cf1d2fb4fc2ef3f 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Maintained by: Alok N Kataria <akataria@vmware.com>
+ * Maintained by: Arvind Kumar <arvindkumar@vmware.com>
  *
  */
 
@@ -26,7 +26,7 @@
 
 #include <linux/types.h>
 
-#define PVSCSI_DRIVER_VERSION_STRING   "1.0.1.0-k"
+#define PVSCSI_DRIVER_VERSION_STRING   "1.0.2.0-k"
 
 #define PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT 128
 
  * host adapter status/error codes
  */
 enum HostBusAdapterStatus {
-   BTSTAT_SUCCESS       = 0x00,  /* CCB complete normally with no errors */
-   BTSTAT_LINKED_COMMAND_COMPLETED           = 0x0a,
-   BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b,
-   BTSTAT_DATA_UNDERRUN = 0x0c,
-   BTSTAT_SELTIMEO      = 0x11,  /* SCSI selection timeout */
-   BTSTAT_DATARUN       = 0x12,  /* data overrun/underrun */
-   BTSTAT_BUSFREE       = 0x13,  /* unexpected bus free */
-   BTSTAT_INVPHASE      = 0x14,  /* invalid bus phase or sequence requested by target */
-   BTSTAT_LUNMISMATCH   = 0x17,  /* linked CCB has different LUN from first CCB */
-   BTSTAT_SENSFAILED    = 0x1b,  /* auto request sense failed */
-   BTSTAT_TAGREJECT     = 0x1c,  /* SCSI II tagged queueing message rejected by target */
-   BTSTAT_BADMSG        = 0x1d,  /* unsupported message received by the host adapter */
-   BTSTAT_HAHARDWARE    = 0x20,  /* host adapter hardware failed */
-   BTSTAT_NORESPONSE    = 0x21,  /* target did not respond to SCSI ATN, sent a SCSI RST */
-   BTSTAT_SENTRST       = 0x22,  /* host adapter asserted a SCSI RST */
-   BTSTAT_RECVRST       = 0x23,  /* other SCSI devices asserted a SCSI RST */
-   BTSTAT_DISCONNECT    = 0x24,  /* target device reconnected improperly (w/o tag) */
-   BTSTAT_BUSRESET      = 0x25,  /* host adapter issued BUS device reset */
-   BTSTAT_ABORTQUEUE    = 0x26,  /* abort queue generated */
-   BTSTAT_HASOFTWARE    = 0x27,  /* host adapter software error */
-   BTSTAT_HATIMEOUT     = 0x30,  /* host adapter hardware timeout error */
-   BTSTAT_SCSIPARITY    = 0x34,  /* SCSI parity error detected */
+       BTSTAT_SUCCESS       = 0x00,  /* CCB complete normally with no errors */
+       BTSTAT_LINKED_COMMAND_COMPLETED           = 0x0a,
+       BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b,
+       BTSTAT_DATA_UNDERRUN = 0x0c,
+       BTSTAT_SELTIMEO      = 0x11,  /* SCSI selection timeout */
+       BTSTAT_DATARUN       = 0x12,  /* data overrun/underrun */
+       BTSTAT_BUSFREE       = 0x13,  /* unexpected bus free */
+       BTSTAT_INVPHASE      = 0x14,  /* invalid bus phase or sequence
+                                      * requested by target */
+       BTSTAT_LUNMISMATCH   = 0x17,  /* linked CCB has different LUN from
+                                      * first CCB */
+       BTSTAT_INVPARAM      = 0x1a,  /* invalid parameter in CCB or segment
+                                      * list */
+       BTSTAT_SENSFAILED    = 0x1b,  /* auto request sense failed */
+       BTSTAT_TAGREJECT     = 0x1c,  /* SCSI II tagged queueing message
+                                      * rejected by target */
+       BTSTAT_BADMSG        = 0x1d,  /* unsupported message received by the
+                                      * host adapter */
+       BTSTAT_HAHARDWARE    = 0x20,  /* host adapter hardware failed */
+       BTSTAT_NORESPONSE    = 0x21,  /* target did not respond to SCSI ATN,
+                                      * sent a SCSI RST */
+       BTSTAT_SENTRST       = 0x22,  /* host adapter asserted a SCSI RST */
+       BTSTAT_RECVRST       = 0x23,  /* other SCSI devices asserted a SCSI
+                                      * RST */
+       BTSTAT_DISCONNECT    = 0x24,  /* target device reconnected improperly
+                                      * (w/o tag) */
+       BTSTAT_BUSRESET      = 0x25,  /* host adapter issued BUS device reset */
+       BTSTAT_ABORTQUEUE    = 0x26,  /* abort queue generated */
+       BTSTAT_HASOFTWARE    = 0x27,  /* host adapter software error */
+       BTSTAT_HATIMEOUT     = 0x30,  /* host adapter hardware timeout error */
+       BTSTAT_SCSIPARITY    = 0x34,  /* SCSI parity error detected */
+};
+
+/*
+ * SCSI device status values.
+ */
+enum ScsiDeviceStatus {
+       SDSTAT_GOOD  = 0x00, /* No errors. */
+       SDSTAT_CHECK = 0x02, /* Check condition. */
 };
 
 /*
@@ -113,6 +130,29 @@ struct PVSCSICmdDescResetDevice {
        u8      lun[8];
 } __packed;
 
+/*
+ * Command descriptor for PVSCSI_CMD_CONFIG --
+ */
+
+struct PVSCSICmdDescConfigCmd {
+       u64 cmpAddr;
+       u64 configPageAddress;
+       u32 configPageNum;
+       u32 _pad;
+} __packed;
+
+enum PVSCSIConfigPageType {
+       PVSCSI_CONFIG_PAGE_CONTROLLER = 0x1958,
+       PVSCSI_CONFIG_PAGE_PHY        = 0x1959,
+       PVSCSI_CONFIG_PAGE_DEVICE     = 0x195a,
+};
+
+enum PVSCSIConfigPageAddressType {
+       PVSCSI_CONFIG_CONTROLLER_ADDRESS = 0x2120,
+       PVSCSI_CONFIG_BUSTARGET_ADDRESS  = 0x2121,
+       PVSCSI_CONFIG_PHY_ADDRESS        = 0x2122,
+};
+
 /*
  * Command descriptor for PVSCSI_CMD_ABORT_CMD --
  *
@@ -332,6 +372,27 @@ struct PVSCSIRingCmpDesc {
        u32     _pad[2];
 } __packed;
 
+struct PVSCSIConfigPageHeader {
+       u32 pageNum;
+       u16 numDwords;
+       u16 hostStatus;
+       u16 scsiStatus;
+       u16 reserved[3];
+} __packed;
+
+struct PVSCSIConfigPageController {
+       struct PVSCSIConfigPageHeader header;
+       u64 nodeWWN; /* Device name as defined in the SAS spec. */
+       u16 manufacturer[64];
+       u16 serialNumber[64];
+       u16 opromVersion[32];
+       u16 hwVersion[32];
+       u16 firmwareVersion[32];
+       u32 numPhys;
+       u8  useConsecutivePhyWWNs;
+       u8  reserved[3];
+} __packed;
+
 /*
  * Interrupt status / IRQ bits.
  */
index 7b246efa94ea657de185facca3f622a47adfb9be..012df2676a267950b77c713eb23afc56b1a341c8 100644 (file)
@@ -2,13 +2,14 @@
  * IRQ chip definitions for INTC IRQs.
  *
  * Copyright (C) 2007, 2008 Magnus Damm
- * Copyright (C) 2009, 2010 Paul Mundt
+ * Copyright (C) 2009 - 2012 Paul Mundt
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
 #include <linux/cpumask.h>
+#include <linux/bsearch.h>
 #include <linux/io.h>
 #include "internals.h"
 
@@ -58,11 +59,6 @@ static void intc_disable(struct irq_data *data)
        }
 }
 
-static int intc_set_wake(struct irq_data *data, unsigned int on)
-{
-       return 0; /* allow wakeup, but setup hardware in intc_suspend() */
-}
-
 #ifdef CONFIG_SMP
 /*
  * This is held with the irq desc lock held, so we don't require any
@@ -78,7 +74,7 @@ static int intc_set_affinity(struct irq_data *data,
 
        cpumask_copy(data->affinity, cpumask);
 
-       return 0;
+       return IRQ_SET_MASK_OK_NOCOPY;
 }
 #endif
 
@@ -122,28 +118,12 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
                                             unsigned int nr_hp,
                                             unsigned int irq)
 {
-       int i;
-
-       /*
-        * this doesn't scale well, but...
-        *
-        * this function should only be used for cerain uncommon
-        * operations such as intc_set_priority() and intc_set_type()
-        * and in those rare cases performance doesn't matter that much.
-        * keeping the memory footprint low is more important.
-        *
-        * one rather simple way to speed this up and still keep the
-        * memory footprint down is to make sure the array is sorted
-        * and then perform a bisect to lookup the irq.
-        */
-       for (i = 0; i < nr_hp; i++) {
-               if ((hp + i)->irq != irq)
-                       continue;
+       struct intc_handle_int key;
 
-               return hp + i;
-       }
+       key.irq = irq;
+       key.handle = 0;
 
-       return NULL;
+       return bsearch(&key, hp, nr_hp, sizeof(*hp), intc_handle_int_cmp);
 }
 
 int intc_set_priority(unsigned int irq, unsigned int prio)
@@ -223,10 +203,9 @@ struct irq_chip intc_irq_chip      = {
        .irq_mask_ack           = intc_mask_ack,
        .irq_enable             = intc_enable,
        .irq_disable            = intc_disable,
-       .irq_shutdown           = intc_disable,
        .irq_set_type           = intc_set_type,
-       .irq_set_wake           = intc_set_wake,
 #ifdef CONFIG_SMP
        .irq_set_affinity       = intc_set_affinity,
 #endif
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
 };
index 2fde8970dfd0716dda4fcf69395da9be7bf80214..7e562ccb699734b3b6ce77579309cbdb87437817 100644 (file)
@@ -2,7 +2,7 @@
  * Shared interrupt handling code for IPR and INTC2 types of IRQs.
  *
  * Copyright (C) 2007, 2008 Magnus Damm
- * Copyright (C) 2009, 2010 Paul Mundt
+ * Copyright (C) 2009 - 2012 Paul Mundt
  *
  * Based on intc2.c and ipr.c
  *
 #include <linux/spinlock.h>
 #include <linux/radix-tree.h>
 #include <linux/export.h>
+#include <linux/sort.h>
 #include "internals.h"
 
 LIST_HEAD(intc_list);
 DEFINE_RAW_SPINLOCK(intc_big_lock);
-unsigned int nr_intc_controllers;
+static unsigned int nr_intc_controllers;
 
 /*
  * Default priority level
@@ -267,6 +268,9 @@ int __init register_intc_controller(struct intc_desc *desc)
                        k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);
                        k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);
                }
+
+               sort(d->prio, hw->nr_prio_regs, sizeof(*d->prio),
+                    intc_handle_int_cmp, NULL);
        }
 
        if (hw->sense_regs) {
@@ -277,6 +281,9 @@ int __init register_intc_controller(struct intc_desc *desc)
 
                for (i = 0; i < hw->nr_sense_regs; i++)
                        k += save_reg(d, k, hw->sense_regs[i].reg, 0);
+
+               sort(d->sense, hw->nr_sense_regs, sizeof(*d->sense),
+                    intc_handle_int_cmp, NULL);
        }
 
        if (hw->subgroups)
index f461d5300b81d77fcc06915669c3eb9b56ea8e91..7863a44918a293e9a4c5d557349775bd072d0cc2 100644 (file)
@@ -172,9 +172,8 @@ intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d,
        return 0;
 }
 
-static unsigned int __init intc_ack_data(struct intc_desc *desc,
-                                         struct intc_desc_int *d,
-                                         intc_enum enum_id)
+static unsigned int intc_ack_data(struct intc_desc *desc,
+                                 struct intc_desc_int *d, intc_enum enum_id)
 {
        struct intc_mask_reg *mr = desc->hw.ack_regs;
        unsigned int i, j, fn, mode;
index b0e9155ff73965bc0da482fb6f86983828ae98c8..f034a979a16f5c15bcd43c9cd9b9c1a069435880 100644 (file)
@@ -108,6 +108,14 @@ static inline void activate_irq(int irq)
 #endif
 }
 
+static inline int intc_handle_int_cmp(const void *a, const void *b)
+{
+       const struct intc_handle_int *_a = a;
+       const struct intc_handle_int *_b = b;
+
+       return _a->irq - _b->irq;
+}
+
 /* access.c */
 extern unsigned long
 (*intc_reg_fns[])(unsigned long addr, unsigned long h, unsigned long data);
@@ -157,7 +165,6 @@ void _intc_enable(struct irq_data *data, unsigned long handle);
 /* core.c */
 extern struct list_head intc_list;
 extern raw_spinlock_t intc_big_lock;
-extern unsigned int nr_intc_controllers;
 extern struct bus_type intc_subsys;
 
 unsigned int intc_get_dfl_prio_level(void);
index 0d82a6d5fa58d2212cc6b4b040a62eddf8316aeb..2d721232467a133ab3727a399b341031e1de5de4 100644 (file)
@@ -52,7 +52,7 @@ Configuration
 
    There is only one option: start_off.
    You can use it by: 'modprobe asus_oled start_off=1', or by adding this
-   line to /etc/modprobe.conf:
+   line to /etc/modprobe.d/asus_oled.conf:
    options asus_oled start_off=1
 
    With this option provided, asus_oled driver will switch off the display
index f7f71b2d3101750fe0a4f2d1fd68dd16aad14269..514a691abea0e2bf7ed517c7651f61474e82b293 100644 (file)
@@ -18,3 +18,11 @@ config THERMAL_HWMON
        depends on THERMAL
        depends on HWMON=y || HWMON=THERMAL
        default y
+
+config SPEAR_THERMAL
+       bool "SPEAr thermal sensor driver"
+       depends on THERMAL
+       depends on PLAT_SPEAR
+       help
+         Enable this to plug the SPEAr thermal sensor driver into the Linux
+         thermal framework
index 31108a01c22e21d659aa68609cda0436f123e448..a9fff0bf4b1486f8754d3168f8b6724752f0553f 100644 (file)
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_THERMAL)          += thermal_sys.o
+obj-$(CONFIG_SPEAR_THERMAL)            += spear_thermal.o
\ No newline at end of file
diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c
new file mode 100644 (file)
index 0000000..c2e32df
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * SPEAr thermal driver.
+ *
+ * Copyright (C) 2011-2012 ST Microelectronics
+ * Author: Vincenzo Frascino <vincenzo.frascino@st.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/spear_thermal.h>
+#include <linux/thermal.h>
+
+#define MD_FACTOR      1000
+
+/* SPEAr Thermal Sensor Dev Structure */
+struct spear_thermal_dev {
+       /* pointer to base address of the thermal sensor */
+       void __iomem *thermal_base;
+       /* clk structure */
+       struct clk *clk;
+       /* pointer to thermal flags */
+       unsigned int flags;
+};
+
+static inline int thermal_get_temp(struct thermal_zone_device *thermal,
+                               unsigned long *temp)
+{
+       struct spear_thermal_dev *stdev = thermal->devdata;
+
+       /*
+        * Data are ready to be read after 628 usec from POWERDOWN signal
+        * (PDN) = 1
+        */
+       *temp = (readl_relaxed(stdev->thermal_base) & 0x7F) * MD_FACTOR;
+       return 0;
+}
+
+static struct thermal_zone_device_ops ops = {
+       .get_temp = thermal_get_temp,
+};
+
+#ifdef CONFIG_PM
+static int spear_thermal_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
+       struct spear_thermal_dev *stdev = spear_thermal->devdata;
+       unsigned int actual_mask = 0;
+
+       /* Disable SPEAr Thermal Sensor */
+       actual_mask = readl_relaxed(stdev->thermal_base);
+       writel_relaxed(actual_mask & ~stdev->flags, stdev->thermal_base);
+
+       clk_disable(stdev->clk);
+       dev_info(dev, "Suspended.\n");
+
+       return 0;
+}
+
+static int spear_thermal_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
+       struct spear_thermal_dev *stdev = spear_thermal->devdata;
+       unsigned int actual_mask = 0;
+       int ret = 0;
+
+       ret = clk_enable(stdev->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't enable clock\n");
+               return ret;
+       }
+
+       /* Enable SPEAr Thermal Sensor */
+       actual_mask = readl_relaxed(stdev->thermal_base);
+       writel_relaxed(actual_mask | stdev->flags, stdev->thermal_base);
+
+       dev_info(dev, "Resumed.\n");
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(spear_thermal_pm_ops, spear_thermal_suspend,
+               spear_thermal_resume);
+
+static int spear_thermal_probe(struct platform_device *pdev)
+{
+       struct thermal_zone_device *spear_thermal = NULL;
+       struct spear_thermal_dev *stdev;
+       struct spear_thermal_pdata *pdata;
+       int ret = 0;
+       struct resource *stres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       if (!stres) {
+               dev_err(&pdev->dev, "memory resource missing\n");
+               return -ENODEV;
+       }
+
+       pdata = dev_get_platdata(&pdev->dev);
+       if (!pdata) {
+               dev_err(&pdev->dev, "platform data is NULL\n");
+               return -EINVAL;
+       }
+
+       stdev = devm_kzalloc(&pdev->dev, sizeof(*stdev), GFP_KERNEL);
+       if (!stdev) {
+               dev_err(&pdev->dev, "kzalloc fail\n");
+               return -ENOMEM;
+       }
+
+       /* Enable thermal sensor */
+       stdev->thermal_base = devm_ioremap(&pdev->dev, stres->start,
+                       resource_size(stres));
+       if (!stdev->thermal_base) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               return -ENOMEM;
+       }
+
+       stdev->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(stdev->clk)) {
+               dev_err(&pdev->dev, "Can't get clock\n");
+               return PTR_ERR(stdev->clk);
+       }
+
+       ret = clk_enable(stdev->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't enable clock\n");
+               goto put_clk;
+       }
+
+       stdev->flags = pdata->thermal_flags;
+       writel_relaxed(stdev->flags, stdev->thermal_base);
+
+       spear_thermal = thermal_zone_device_register("spear_thermal", 0,
+                               stdev, &ops, 0, 0, 0, 0);
+       if (IS_ERR(spear_thermal)) {
+               dev_err(&pdev->dev, "thermal zone device is NULL\n");
+               ret = PTR_ERR(spear_thermal);
+               goto disable_clk;
+       }
+
+       platform_set_drvdata(pdev, spear_thermal);
+
+       dev_info(&spear_thermal->device, "Thermal Sensor Loaded at: 0x%p.\n",
+                       stdev->thermal_base);
+
+       return 0;
+
+disable_clk:
+       clk_disable(stdev->clk);
+put_clk:
+       clk_put(stdev->clk);
+
+       return ret;
+}
+
+static int spear_thermal_exit(struct platform_device *pdev)
+{
+       unsigned int actual_mask = 0;
+       struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
+       struct spear_thermal_dev *stdev = spear_thermal->devdata;
+
+       thermal_zone_device_unregister(spear_thermal);
+       platform_set_drvdata(pdev, NULL);
+
+       /* Disable SPEAr Thermal Sensor */
+       actual_mask = readl_relaxed(stdev->thermal_base);
+       writel_relaxed(actual_mask & ~stdev->flags, stdev->thermal_base);
+
+       clk_disable(stdev->clk);
+       clk_put(stdev->clk);
+
+       return 0;
+}
+
+static struct platform_driver spear_thermal_driver = {
+       .probe = spear_thermal_probe,
+       .remove = spear_thermal_exit,
+       .driver = {
+               .name = "spear_thermal",
+               .owner = THIS_MODULE,
+               .pm = &spear_thermal_pm_ops,
+       },
+};
+
+module_platform_driver(spear_thermal_driver);
+
+MODULE_AUTHOR("Vincenzo Frascino <vincenzo.frascino@st.com>");
+MODULE_DESCRIPTION("SPEAr thermal driver");
+MODULE_LICENSE("GPL");
index 220ce7e31cf50faa89fd35a46946111b523da0d5..022bacb71a7ede51571a6976830859d3f674bb71 100644 (file)
@@ -23,6 +23,8 @@
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/err.h>
@@ -39,8 +41,6 @@ MODULE_AUTHOR("Zhang Rui");
 MODULE_DESCRIPTION("Generic thermal management sysfs support");
 MODULE_LICENSE("GPL");
 
-#define PREFIX "Thermal: "
-
 struct thermal_cooling_device_instance {
        int id;
        char name[THERMAL_NAME_LENGTH];
@@ -60,13 +60,11 @@ static LIST_HEAD(thermal_tz_list);
 static LIST_HEAD(thermal_cdev_list);
 static DEFINE_MUTEX(thermal_list_lock);
 
-static unsigned int thermal_event_seqnum;
-
 static int get_idr(struct idr *idr, struct mutex *lock, int *id)
 {
        int err;
 
-      again:
+again:
        if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
                return -ENOMEM;
 
@@ -152,9 +150,9 @@ mode_store(struct device *dev, struct device_attribute *attr,
        if (!tz->ops->set_mode)
                return -EPERM;
 
-       if (!strncmp(buf, "enabled", sizeof("enabled")))
+       if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
                result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
-       else if (!strncmp(buf, "disabled", sizeof("disabled")))
+       else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
                result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
        else
                result = -EINVAL;
@@ -283,8 +281,7 @@ passive_show(struct device *dev, struct device_attribute *attr,
 static DEVICE_ATTR(type, 0444, type_show, NULL);
 static DEVICE_ATTR(temp, 0444, temp_show, NULL);
 static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
-static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, \
-                  passive_store);
+static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
 
 static struct device_attribute trip_point_attrs[] = {
        __ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
@@ -313,22 +310,6 @@ static struct device_attribute trip_point_attrs[] = {
        __ATTR(trip_point_11_temp, 0444, trip_point_temp_show, NULL),
 };
 
-#define TRIP_POINT_ATTR_ADD(_dev, _index, result)     \
-do {    \
-       result = device_create_file(_dev,       \
-                               &trip_point_attrs[_index * 2]); \
-       if (result)     \
-               break;  \
-       result = device_create_file(_dev,       \
-                       &trip_point_attrs[_index * 2 + 1]);     \
-} while (0)
-
-#define TRIP_POINT_ATTR_REMOVE(_dev, _index)   \
-do {   \
-       device_remove_file(_dev, &trip_point_attrs[_index * 2]);        \
-       device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]);    \
-} while (0)
-
 /* sys I/F for cooling device */
 #define to_cooling_device(_dev)        \
        container_of(_dev, struct thermal_cooling_device, device)
@@ -835,15 +816,14 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
                return 0;
 
        device_remove_file(&tz->device, &dev->attr);
-      remove_symbol_link:
+remove_symbol_link:
        sysfs_remove_link(&tz->device.kobj, dev->name);
-      release_idr:
+release_idr:
        release_idr(&tz->idr, &tz->lock, dev->id);
-      free_mem:
+free_mem:
        kfree(dev);
        return result;
 }
-
 EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
 
 /**
@@ -873,14 +853,13 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
 
        return -ENODEV;
 
-      unbind:
+unbind:
        device_remove_file(&tz->device, &pos->attr);
        sysfs_remove_link(&tz->device.kobj, pos->name);
        release_idr(&tz->idr, &tz->lock, pos->id);
        kfree(pos);
        return 0;
 }
-
 EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
 
 static void thermal_release(struct device *dev)
@@ -888,7 +867,8 @@ static void thermal_release(struct device *dev)
        struct thermal_zone_device *tz;
        struct thermal_cooling_device *cdev;
 
-       if (!strncmp(dev_name(dev), "thermal_zone", sizeof "thermal_zone" - 1)) {
+       if (!strncmp(dev_name(dev), "thermal_zone",
+                    sizeof("thermal_zone") - 1)) {
                tz = to_thermal_zone(dev);
                kfree(tz);
        } else {
@@ -908,8 +888,9 @@ static struct class thermal_class = {
  * @devdata:   device private data.
  * @ops:               standard thermal cooling devices callbacks.
  */
-struct thermal_cooling_device *thermal_cooling_device_register(
-     char *type, void *devdata, const struct thermal_cooling_device_ops *ops)
+struct thermal_cooling_device *
+thermal_cooling_device_register(char *type, void *devdata,
+                               const struct thermal_cooling_device_ops *ops)
 {
        struct thermal_cooling_device *cdev;
        struct thermal_zone_device *pos;
@@ -974,12 +955,11 @@ struct thermal_cooling_device *thermal_cooling_device_register(
        if (!result)
                return cdev;
 
-      unregister:
+unregister:
        release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
        device_unregister(&cdev->device);
        return ERR_PTR(result);
 }
-
 EXPORT_SYMBOL(thermal_cooling_device_register);
 
 /**
@@ -1024,7 +1004,6 @@ void thermal_cooling_device_unregister(struct
        device_unregister(&cdev->device);
        return;
 }
-
 EXPORT_SYMBOL(thermal_cooling_device_unregister);
 
 /**
@@ -1044,8 +1023,7 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
 
        if (tz->ops->get_temp(tz, &temp)) {
                /* get_temp failed - retry it later */
-               printk(KERN_WARNING PREFIX "failed to read out thermal zone "
-                      "%d\n", tz->id);
+               pr_warn("failed to read out thermal zone %d\n", tz->id);
                goto leave;
        }
 
@@ -1060,9 +1038,8 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
                                        ret = tz->ops->notify(tz, count,
                                                              trip_type);
                                if (!ret) {
-                                       printk(KERN_EMERG
-                                              "Critical temperature reached (%ld C), shutting down.\n",
-                                              temp/1000);
+                                       pr_emerg("Critical temperature reached (%ld C), shutting down\n",
+                                                temp/1000);
                                        orderly_poweroff(true);
                                }
                        }
@@ -1100,7 +1077,7 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
 
        tz->last_temperature = temp;
 
-      leave:
+leave:
        if (tz->passive)
                thermal_zone_device_set_polling(tz, tz->passive_delay);
        else if (tz->polling_delay)
@@ -1199,7 +1176,12 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
        }
 
        for (count = 0; count < trips; count++) {
-               TRIP_POINT_ATTR_ADD(&tz->device, count, result);
+               result = device_create_file(&tz->device,
+                                           &trip_point_attrs[count * 2]);
+               if (result)
+                       break;
+               result = device_create_file(&tz->device,
+                                           &trip_point_attrs[count * 2 + 1]);
                if (result)
                        goto unregister;
                tz->ops->get_trip_type(tz, count, &trip_type);
@@ -1235,12 +1217,11 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
        if (!result)
                return tz;
 
-      unregister:
+unregister:
        release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
        device_unregister(&tz->device);
        return ERR_PTR(result);
 }
-
 EXPORT_SYMBOL(thermal_zone_device_register);
 
 /**
@@ -1279,9 +1260,12 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
        if (tz->ops->get_mode)
                device_remove_file(&tz->device, &dev_attr_mode);
 
-       for (count = 0; count < tz->trips; count++)
-               TRIP_POINT_ATTR_REMOVE(&tz->device, count);
-
+       for (count = 0; count < tz->trips; count++) {
+               device_remove_file(&tz->device,
+                                  &trip_point_attrs[count * 2]);
+               device_remove_file(&tz->device,
+                                  &trip_point_attrs[count * 2 + 1]);
+       }
        thermal_remove_hwmon_sysfs(tz);
        release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
        idr_destroy(&tz->idr);
@@ -1289,7 +1273,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
        device_unregister(&tz->device);
        return;
 }
-
 EXPORT_SYMBOL(thermal_zone_device_unregister);
 
 #ifdef CONFIG_NET
@@ -1312,10 +1295,11 @@ int thermal_generate_netlink_event(u32 orig, enum events event)
        void *msg_header;
        int size;
        int result;
+       static unsigned int thermal_event_seqnum;
 
        /* allocate memory */
-       size = nla_total_size(sizeof(struct thermal_genl_event)) + \
-                               nla_total_size(0);
+       size = nla_total_size(sizeof(struct thermal_genl_event)) +
+              nla_total_size(0);
 
        skb = genlmsg_new(size, GFP_ATOMIC);
        if (!skb)
@@ -1331,8 +1315,8 @@ int thermal_generate_netlink_event(u32 orig, enum events event)
        }
 
        /* fill the data */
-       attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT, \
-                       sizeof(struct thermal_genl_event));
+       attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT,
+                          sizeof(struct thermal_genl_event));
 
        if (!attr) {
                nlmsg_free(skb);
@@ -1359,7 +1343,7 @@ int thermal_generate_netlink_event(u32 orig, enum events event)
 
        result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
        if (result)
-               printk(KERN_INFO "failed to send netlink event:%d", result);
+               pr_info("failed to send netlink event:%d\n", result);
 
        return result;
 }
index 794ecb40017ce2257a0b5babac7262ae52c43ed1..e1235accab740b4bca4eb01c28b0ed76e170ea93 100644 (file)
  *     You can find the original tools for this direct from Multitech
  *             ftp://ftp.multitech.com/ISI-Cards/
  *
- *     Having installed the cards the module options (/etc/modprobe.conf)
+ *     Having installed the cards the module options (/etc/modprobe.d/)
  *
  *     options isicom   io=card1,card2,card3,card4 irq=card1,card2,card3,card4
  *
index f8db8a70c14eaaa78f6c9571b346de456c619bc8..bf461cf99616c04aa9e2655eaee8e62d21c782db 100644 (file)
@@ -1229,17 +1229,20 @@ static void sci_dma_tx_complete(void *arg)
        port->icount.tx += sg_dma_len(&s->sg_tx);
 
        async_tx_ack(s->desc_tx);
-       s->cookie_tx = -EINVAL;
        s->desc_tx = NULL;
 
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(port);
 
        if (!uart_circ_empty(xmit)) {
+               s->cookie_tx = 0;
                schedule_work(&s->work_tx);
-       } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
-               u16 ctrl = sci_in(port, SCSCR);
-               sci_out(port, SCSCR, ctrl & ~SCSCR_TIE);
+       } else {
+               s->cookie_tx = -EINVAL;
+               if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+                       u16 ctrl = sci_in(port, SCSCR);
+                       sci_out(port, SCSCR, ctrl & ~SCSCR_TIE);
+               }
        }
 
        spin_unlock_irqrestore(&port->lock, flags);
@@ -1501,8 +1504,10 @@ static void sci_start_tx(struct uart_port *port)
        }
 
        if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) &&
-           s->cookie_tx < 0)
+           s->cookie_tx < 0) {
+               s->cookie_tx = 0;
                schedule_work(&s->work_tx);
+       }
 #endif
 
        if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
index 7c229d30468473dd745d87843b6faacf7e35f72f..ff8605b4b4be4679a7d6b9abf177bba8ee7f630b 100644 (file)
@@ -1724,7 +1724,8 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv)
 
 /*
  * Module parameter to control latency timer for NDI FTDI-based USB devices.
- * If this value is not set in modprobe.conf.local its value will be set to 1ms.
+ * If this value is not set in /etc/modprobe.d/ its value will be set
+ * to 1ms.
  */
 static int ndi_latency_timer = 1;
 
index fe2d803a6347c1d2effa054e7863ba38be9e8d98..7691c866637be6be835442396b1f545da5a7f09c 100644 (file)
@@ -222,7 +222,7 @@ config USB_LIBUSUAL
          for usb-storage and ub drivers, and allows to switch binding
          of these devices without rebuilding modules.
 
-         Typical syntax of /etc/modprobe.conf is:
+         Typical syntax of /etc/modprobe.d/*conf is:
 
                options libusual bias="ub"
 
index 0cc20b35c1c4d99bd424dc720e5764c9f80ac7ba..42704149b72343f720af7844ec1c36a51da4140e 100644 (file)
@@ -171,11 +171,11 @@ out:
        spin_unlock_irqrestore(&workers->lock, flags);
 }
 
-static noinline int run_ordered_completions(struct btrfs_workers *workers,
+static noinline void run_ordered_completions(struct btrfs_workers *workers,
                                            struct btrfs_work *work)
 {
        if (!workers->ordered)
-               return 0;
+               return;
 
        set_bit(WORK_DONE_BIT, &work->flags);
 
@@ -213,7 +213,6 @@ static noinline int run_ordered_completions(struct btrfs_workers *workers,
        }
 
        spin_unlock(&workers->order_lock);
-       return 0;
 }
 
 static void put_worker(struct btrfs_worker_thread *worker)
@@ -399,7 +398,7 @@ again:
 /*
  * this will wait for all the worker threads to shutdown
  */
-int btrfs_stop_workers(struct btrfs_workers *workers)
+void btrfs_stop_workers(struct btrfs_workers *workers)
 {
        struct list_head *cur;
        struct btrfs_worker_thread *worker;
@@ -427,7 +426,6 @@ int btrfs_stop_workers(struct btrfs_workers *workers)
                put_worker(worker);
        }
        spin_unlock_irq(&workers->lock);
-       return 0;
 }
 
 /*
@@ -615,14 +613,14 @@ found:
  * it was taken from.  It is intended for use with long running work functions
  * that make some progress and want to give the cpu up for others.
  */
-int btrfs_requeue_work(struct btrfs_work *work)
+void btrfs_requeue_work(struct btrfs_work *work)
 {
        struct btrfs_worker_thread *worker = work->worker;
        unsigned long flags;
        int wake = 0;
 
        if (test_and_set_bit(WORK_QUEUED_BIT, &work->flags))
-               goto out;
+               return;
 
        spin_lock_irqsave(&worker->lock, flags);
        if (test_bit(WORK_HIGH_PRIO_BIT, &work->flags))
@@ -649,9 +647,6 @@ int btrfs_requeue_work(struct btrfs_work *work)
        if (wake)
                wake_up_process(worker->task);
        spin_unlock_irqrestore(&worker->lock, flags);
-out:
-
-       return 0;
 }
 
 void btrfs_set_work_high_prio(struct btrfs_work *work)
index f34cc31fa3c9a8d1c55f7181ca05b10a11f8ba64..063698b90ce2dd0481663aeee46b30ef796d52e2 100644 (file)
@@ -111,9 +111,9 @@ struct btrfs_workers {
 
 void btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work);
 int btrfs_start_workers(struct btrfs_workers *workers);
-int btrfs_stop_workers(struct btrfs_workers *workers);
+void btrfs_stop_workers(struct btrfs_workers *workers);
 void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max,
                        struct btrfs_workers *async_starter);
-int btrfs_requeue_work(struct btrfs_work *work);
+void btrfs_requeue_work(struct btrfs_work *work);
 void btrfs_set_work_high_prio(struct btrfs_work *work);
 #endif
index 0436c12da8c2e7d551430639f09e086182b85614..f4e90748940abf6c1f36f3186177e4640bd24546 100644 (file)
@@ -116,6 +116,7 @@ add_parent:
  * to a logical address
  */
 static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
+                                       int search_commit_root,
                                        struct __prelim_ref *ref,
                                        struct ulist *parents)
 {
@@ -131,6 +132,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
+       path->search_commit_root = !!search_commit_root;
 
        root_key.objectid = ref->root_id;
        root_key.type = BTRFS_ROOT_ITEM_KEY;
@@ -188,6 +190,7 @@ out:
  * resolve all indirect backrefs from the list
  */
 static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
+                                  int search_commit_root,
                                   struct list_head *head)
 {
        int err;
@@ -212,7 +215,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
                        continue;
                if (ref->count == 0)
                        continue;
-               err = __resolve_indirect_ref(fs_info, ref, parents);
+               err = __resolve_indirect_ref(fs_info, search_commit_root,
+                                            ref, parents);
                if (err) {
                        if (ret == 0)
                                ret = err;
@@ -586,6 +590,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
        struct btrfs_delayed_ref_head *head;
        int info_level = 0;
        int ret;
+       int search_commit_root = (trans == BTRFS_BACKREF_SEARCH_COMMIT_ROOT);
        struct list_head prefs_delayed;
        struct list_head prefs;
        struct __prelim_ref *ref;
@@ -600,6 +605,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
+       path->search_commit_root = !!search_commit_root;
 
        /*
         * grab both a lock on the path and a lock on the delayed ref head.
@@ -614,35 +620,39 @@ again:
                goto out;
        BUG_ON(ret == 0);
 
-       /*
-        * look if there are updates for this ref queued and lock the head
-        */
-       delayed_refs = &trans->transaction->delayed_refs;
-       spin_lock(&delayed_refs->lock);
-       head = btrfs_find_delayed_ref_head(trans, bytenr);
-       if (head) {
-               if (!mutex_trylock(&head->mutex)) {
-                       atomic_inc(&head->node.refs);
-                       spin_unlock(&delayed_refs->lock);
-
-                       btrfs_release_path(path);
-
-                       /*
-                        * Mutex was contended, block until it's
-                        * released and try again
-                        */
-                       mutex_lock(&head->mutex);
-                       mutex_unlock(&head->mutex);
-                       btrfs_put_delayed_ref(&head->node);
-                       goto again;
-               }
-               ret = __add_delayed_refs(head, seq, &info_key, &prefs_delayed);
-               if (ret) {
-                       spin_unlock(&delayed_refs->lock);
-                       goto out;
+       if (trans != BTRFS_BACKREF_SEARCH_COMMIT_ROOT) {
+               /*
+                * look if there are updates for this ref queued and lock the
+                * head
+                */
+               delayed_refs = &trans->transaction->delayed_refs;
+               spin_lock(&delayed_refs->lock);
+               head = btrfs_find_delayed_ref_head(trans, bytenr);
+               if (head) {
+                       if (!mutex_trylock(&head->mutex)) {
+                               atomic_inc(&head->node.refs);
+                               spin_unlock(&delayed_refs->lock);
+
+                               btrfs_release_path(path);
+
+                               /*
+                                * Mutex was contended, block until it's
+                                * released and try again
+                                */
+                               mutex_lock(&head->mutex);
+                               mutex_unlock(&head->mutex);
+                               btrfs_put_delayed_ref(&head->node);
+                               goto again;
+                       }
+                       ret = __add_delayed_refs(head, seq, &info_key,
+                                                &prefs_delayed);
+                       if (ret) {
+                               spin_unlock(&delayed_refs->lock);
+                               goto out;
+                       }
                }
+               spin_unlock(&delayed_refs->lock);
        }
-       spin_unlock(&delayed_refs->lock);
 
        if (path->slots[0]) {
                struct extent_buffer *leaf;
@@ -679,7 +689,7 @@ again:
        if (ret)
                goto out;
 
-       ret = __resolve_indirect_refs(fs_info, &prefs);
+       ret = __resolve_indirect_refs(fs_info, search_commit_root, &prefs);
        if (ret)
                goto out;
 
@@ -1074,8 +1084,7 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
        return 0;
 }
 
-static int iterate_leaf_refs(struct btrfs_fs_info *fs_info,
-                               struct btrfs_path *path, u64 logical,
+static int iterate_leaf_refs(struct btrfs_fs_info *fs_info, u64 logical,
                                u64 orig_extent_item_objectid,
                                u64 extent_item_pos, u64 root,
                                iterate_extent_inodes_t *iterate, void *ctx)
@@ -1143,35 +1152,38 @@ static int iterate_leaf_refs(struct btrfs_fs_info *fs_info,
  * calls iterate() for every inode that references the extent identified by
  * the given parameters.
  * when the iterator function returns a non-zero value, iteration stops.
- * path is guaranteed to be in released state when iterate() is called.
  */
 int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
-                               struct btrfs_path *path,
                                u64 extent_item_objectid, u64 extent_item_pos,
+                               int search_commit_root,
                                iterate_extent_inodes_t *iterate, void *ctx)
 {
        int ret;
        struct list_head data_refs = LIST_HEAD_INIT(data_refs);
        struct list_head shared_refs = LIST_HEAD_INIT(shared_refs);
        struct btrfs_trans_handle *trans;
-       struct ulist *refs;
-       struct ulist *roots;
+       struct ulist *refs = NULL;
+       struct ulist *roots = NULL;
        struct ulist_node *ref_node = NULL;
        struct ulist_node *root_node = NULL;
        struct seq_list seq_elem;
-       struct btrfs_delayed_ref_root *delayed_refs;
-
-       trans = btrfs_join_transaction(fs_info->extent_root);
-       if (IS_ERR(trans))
-               return PTR_ERR(trans);
+       struct btrfs_delayed_ref_root *delayed_refs = NULL;
 
        pr_debug("resolving all inodes for extent %llu\n",
                        extent_item_objectid);
 
-       delayed_refs = &trans->transaction->delayed_refs;
-       spin_lock(&delayed_refs->lock);
-       btrfs_get_delayed_seq(delayed_refs, &seq_elem);
-       spin_unlock(&delayed_refs->lock);
+       if (search_commit_root) {
+               trans = BTRFS_BACKREF_SEARCH_COMMIT_ROOT;
+       } else {
+               trans = btrfs_join_transaction(fs_info->extent_root);
+               if (IS_ERR(trans))
+                       return PTR_ERR(trans);
+
+               delayed_refs = &trans->transaction->delayed_refs;
+               spin_lock(&delayed_refs->lock);
+               btrfs_get_delayed_seq(delayed_refs, &seq_elem);
+               spin_unlock(&delayed_refs->lock);
+       }
 
        ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid,
                                   extent_item_pos, seq_elem.seq,
@@ -1188,7 +1200,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
                while (!ret && (root_node = ulist_next(roots, root_node))) {
                        pr_debug("root %llu references leaf %llu\n",
                                        root_node->val, ref_node->val);
-                       ret = iterate_leaf_refs(fs_info, path, ref_node->val,
+                       ret = iterate_leaf_refs(fs_info, ref_node->val,
                                                extent_item_objectid,
                                                extent_item_pos, root_node->val,
                                                iterate, ctx);
@@ -1198,8 +1210,11 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
        ulist_free(refs);
        ulist_free(roots);
 out:
-       btrfs_put_delayed_seq(delayed_refs, &seq_elem);
-       btrfs_end_transaction(trans, fs_info->extent_root);
+       if (!search_commit_root) {
+               btrfs_put_delayed_seq(delayed_refs, &seq_elem);
+               btrfs_end_transaction(trans, fs_info->extent_root);
+       }
+
        return ret;
 }
 
@@ -1210,6 +1225,7 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
        int ret;
        u64 extent_item_pos;
        struct btrfs_key found_key;
+       int search_commit_root = path->search_commit_root;
 
        ret = extent_from_logical(fs_info, logical, path,
                                        &found_key);
@@ -1220,8 +1236,9 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
                return ret;
 
        extent_item_pos = logical - found_key.objectid;
-       ret = iterate_extent_inodes(fs_info, path, found_key.objectid,
-                                       extent_item_pos, iterate, ctx);
+       ret = iterate_extent_inodes(fs_info, found_key.objectid,
+                                       extent_item_pos, search_commit_root,
+                                       iterate, ctx);
 
        return ret;
 }
@@ -1342,12 +1359,6 @@ int paths_from_inode(u64 inum, struct inode_fs_paths *ipath)
                                inode_to_path, ipath);
 }
 
-/*
- * allocates space to return multiple file system paths for an inode.
- * total_bytes to allocate are passed, note that space usable for actual path
- * information will be total_bytes - sizeof(struct inode_fs_paths).
- * the returned pointer must be freed with free_ipath() in the end.
- */
 struct btrfs_data_container *init_data_container(u32 total_bytes)
 {
        struct btrfs_data_container *data;
@@ -1403,5 +1414,6 @@ struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root,
 
 void free_ipath(struct inode_fs_paths *ipath)
 {
+       kfree(ipath->fspath);
        kfree(ipath);
 }
index d00dfa9ca9342c96f5057af09fb06418cd943cb6..57ea2e959e4dcfaba89e4ee0b833f5744c3639d3 100644 (file)
@@ -22,6 +22,8 @@
 #include "ioctl.h"
 #include "ulist.h"
 
+#define BTRFS_BACKREF_SEARCH_COMMIT_ROOT ((struct btrfs_trans_handle *)0)
+
 struct inode_fs_paths {
        struct btrfs_path               *btrfs_path;
        struct btrfs_root               *fs_root;
@@ -44,9 +46,8 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
                                u64 *out_root, u8 *out_level);
 
 int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
-                               struct btrfs_path *path,
                                u64 extent_item_objectid,
-                               u64 extent_offset,
+                               u64 extent_offset, int search_commit_root,
                                iterate_extent_inodes_t *iterate, void *ctx);
 
 int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
index b805afb37fa8323255fc90797ed987035ae42a10..d286b40a56715edbddb68c3ba1e5d4403ab94153 100644 (file)
@@ -226,8 +226,8 @@ out:
  * Clear the writeback bits on all of the file
  * pages for a compressed write
  */
-static noinline int end_compressed_writeback(struct inode *inode, u64 start,
-                                            unsigned long ram_size)
+static noinline void end_compressed_writeback(struct inode *inode, u64 start,
+                                             unsigned long ram_size)
 {
        unsigned long index = start >> PAGE_CACHE_SHIFT;
        unsigned long end_index = (start + ram_size - 1) >> PAGE_CACHE_SHIFT;
@@ -253,7 +253,6 @@ static noinline int end_compressed_writeback(struct inode *inode, u64 start,
                index += ret;
        }
        /* the inode may be gone now */
-       return 0;
 }
 
 /*
@@ -392,16 +391,16 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
                         */
                        atomic_inc(&cb->pending_bios);
                        ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
 
                        if (!skip_sum) {
                                ret = btrfs_csum_one_bio(root, inode, bio,
                                                         start, 1);
-                               BUG_ON(ret);
+                               BUG_ON(ret); /* -ENOMEM */
                        }
 
                        ret = btrfs_map_bio(root, WRITE, bio, 0, 1);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
 
                        bio_put(bio);
 
@@ -421,15 +420,15 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
        bio_get(bio);
 
        ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
-       BUG_ON(ret);
+       BUG_ON(ret); /* -ENOMEM */
 
        if (!skip_sum) {
                ret = btrfs_csum_one_bio(root, inode, bio, start, 1);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
 
        ret = btrfs_map_bio(root, WRITE, bio, 0, 1);
-       BUG_ON(ret);
+       BUG_ON(ret); /* -ENOMEM */
 
        bio_put(bio);
        return 0;
@@ -497,7 +496,7 @@ static noinline int add_ra_bio_pages(struct inode *inode,
                 * sure they map to this compressed extent on disk.
                 */
                set_page_extent_mapped(page);
-               lock_extent(tree, last_offset, end, GFP_NOFS);
+               lock_extent(tree, last_offset, end);
                read_lock(&em_tree->lock);
                em = lookup_extent_mapping(em_tree, last_offset,
                                           PAGE_CACHE_SIZE);
@@ -507,7 +506,7 @@ static noinline int add_ra_bio_pages(struct inode *inode,
                    (last_offset + PAGE_CACHE_SIZE > extent_map_end(em)) ||
                    (em->block_start >> 9) != cb->orig_bio->bi_sector) {
                        free_extent_map(em);
-                       unlock_extent(tree, last_offset, end, GFP_NOFS);
+                       unlock_extent(tree, last_offset, end);
                        unlock_page(page);
                        page_cache_release(page);
                        break;
@@ -535,7 +534,7 @@ static noinline int add_ra_bio_pages(struct inode *inode,
                        nr_pages++;
                        page_cache_release(page);
                } else {
-                       unlock_extent(tree, last_offset, end, GFP_NOFS);
+                       unlock_extent(tree, last_offset, end);
                        unlock_page(page);
                        page_cache_release(page);
                        break;
@@ -662,7 +661,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
                        bio_get(comp_bio);
 
                        ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
 
                        /*
                         * inc the count before we submit the bio so
@@ -675,14 +674,14 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
                        if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
                                ret = btrfs_lookup_bio_sums(root, inode,
                                                        comp_bio, sums);
-                               BUG_ON(ret);
+                               BUG_ON(ret); /* -ENOMEM */
                        }
                        sums += (comp_bio->bi_size + root->sectorsize - 1) /
                                root->sectorsize;
 
                        ret = btrfs_map_bio(root, READ, comp_bio,
                                            mirror_num, 0);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
 
                        bio_put(comp_bio);
 
@@ -698,15 +697,15 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
        bio_get(comp_bio);
 
        ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0);
-       BUG_ON(ret);
+       BUG_ON(ret); /* -ENOMEM */
 
        if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
                ret = btrfs_lookup_bio_sums(root, inode, comp_bio, sums);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
 
        ret = btrfs_map_bio(root, READ, comp_bio, mirror_num, 0);
-       BUG_ON(ret);
+       BUG_ON(ret); /* -ENOMEM */
 
        bio_put(comp_bio);
        return 0;
@@ -734,7 +733,7 @@ struct btrfs_compress_op *btrfs_compress_op[] = {
        &btrfs_lzo_compress,
 };
 
-int __init btrfs_init_compress(void)
+void __init btrfs_init_compress(void)
 {
        int i;
 
@@ -744,7 +743,6 @@ int __init btrfs_init_compress(void)
                atomic_set(&comp_alloc_workspace[i], 0);
                init_waitqueue_head(&comp_workspace_wait[i]);
        }
-       return 0;
 }
 
 /*
index a12059f4f0fd3c70fd6302abe4ab3e83fa5797fb..9afb0a62ae82bcf15113a9eda184896caa5d6c80 100644 (file)
@@ -19,7 +19,7 @@
 #ifndef __BTRFS_COMPRESSION_
 #define __BTRFS_COMPRESSION_
 
-int btrfs_init_compress(void);
+void btrfs_init_compress(void);
 void btrfs_exit_compress(void);
 
 int btrfs_compress_pages(int type, struct address_space *mapping,
index 0639a555e16ed1975702ed5509dc9bc1c4dbf490..e801f226d7e028b72ca08b5726ed409215c27b84 100644 (file)
@@ -36,7 +36,7 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root,
                              struct extent_buffer *dst_buf,
                              struct extent_buffer *src_buf);
-static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+static void del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                   struct btrfs_path *path, int level, int slot);
 
 struct btrfs_path *btrfs_alloc_path(void)
@@ -156,10 +156,23 @@ struct extent_buffer *btrfs_root_node(struct btrfs_root *root)
 {
        struct extent_buffer *eb;
 
-       rcu_read_lock();
-       eb = rcu_dereference(root->node);
-       extent_buffer_get(eb);
-       rcu_read_unlock();
+       while (1) {
+               rcu_read_lock();
+               eb = rcu_dereference(root->node);
+
+               /*
+                * RCU really hurts here, we could free up the root node because
+                * it was cow'ed but we may not get the new root node yet so do
+                * the inc_not_zero dance and if it doesn't work then
+                * synchronize_rcu and try again.
+                */
+               if (atomic_inc_not_zero(&eb->refs)) {
+                       rcu_read_unlock();
+                       break;
+               }
+               rcu_read_unlock();
+               synchronize_rcu();
+       }
        return eb;
 }
 
@@ -331,8 +344,13 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
        if (btrfs_block_can_be_shared(root, buf)) {
                ret = btrfs_lookup_extent_info(trans, root, buf->start,
                                               buf->len, &refs, &flags);
-               BUG_ON(ret);
-               BUG_ON(refs == 0);
+               if (ret)
+                       return ret;
+               if (refs == 0) {
+                       ret = -EROFS;
+                       btrfs_std_error(root->fs_info, ret);
+                       return ret;
+               }
        } else {
                refs = 1;
                if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID ||
@@ -351,14 +369,14 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
                     root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) &&
                    !(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) {
                        ret = btrfs_inc_ref(trans, root, buf, 1, 1);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
 
                        if (root->root_key.objectid ==
                            BTRFS_TREE_RELOC_OBJECTID) {
                                ret = btrfs_dec_ref(trans, root, buf, 0, 1);
-                               BUG_ON(ret);
+                               BUG_ON(ret); /* -ENOMEM */
                                ret = btrfs_inc_ref(trans, root, cow, 1, 1);
-                               BUG_ON(ret);
+                               BUG_ON(ret); /* -ENOMEM */
                        }
                        new_flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
                } else {
@@ -368,14 +386,15 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
                                ret = btrfs_inc_ref(trans, root, cow, 1, 1);
                        else
                                ret = btrfs_inc_ref(trans, root, cow, 0, 1);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
                }
                if (new_flags != 0) {
                        ret = btrfs_set_disk_extent_flags(trans, root,
                                                          buf->start,
                                                          buf->len,
                                                          new_flags, 0);
-                       BUG_ON(ret);
+                       if (ret)
+                               return ret;
                }
        } else {
                if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) {
@@ -384,9 +403,9 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
                                ret = btrfs_inc_ref(trans, root, cow, 1, 1);
                        else
                                ret = btrfs_inc_ref(trans, root, cow, 0, 1);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
                        ret = btrfs_dec_ref(trans, root, buf, 1, 1);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
                }
                clean_tree_block(trans, root, buf);
                *last_ref = 1;
@@ -415,7 +434,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
 {
        struct btrfs_disk_key disk_key;
        struct extent_buffer *cow;
-       int level;
+       int level, ret;
        int last_ref = 0;
        int unlock_orig = 0;
        u64 parent_start;
@@ -467,7 +486,11 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
                            (unsigned long)btrfs_header_fsid(cow),
                            BTRFS_FSID_SIZE);
 
-       update_ref_for_cow(trans, root, buf, cow, &last_ref);
+       ret = update_ref_for_cow(trans, root, buf, cow, &last_ref);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               return ret;
+       }
 
        if (root->ref_cows)
                btrfs_reloc_cow_block(trans, root, buf, cow);
@@ -504,7 +527,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
        }
        if (unlock_orig)
                btrfs_tree_unlock(buf);
-       free_extent_buffer(buf);
+       free_extent_buffer_stale(buf);
        btrfs_mark_buffer_dirty(cow);
        *cow_ret = cow;
        return 0;
@@ -934,7 +957,12 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
 
                /* promote the child to a root */
                child = read_node_slot(root, mid, 0);
-               BUG_ON(!child);
+               if (!child) {
+                       ret = -EROFS;
+                       btrfs_std_error(root->fs_info, ret);
+                       goto enospc;
+               }
+
                btrfs_tree_lock(child);
                btrfs_set_lock_blocking(child);
                ret = btrfs_cow_block(trans, root, child, mid, 0, &child);
@@ -959,7 +987,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
                root_sub_used(root, mid->len);
                btrfs_free_tree_block(trans, root, mid, 0, 1, 0);
                /* once for the root ptr */
-               free_extent_buffer(mid);
+               free_extent_buffer_stale(mid);
                return 0;
        }
        if (btrfs_header_nritems(mid) >
@@ -1010,13 +1038,10 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
                if (btrfs_header_nritems(right) == 0) {
                        clean_tree_block(trans, root, right);
                        btrfs_tree_unlock(right);
-                       wret = del_ptr(trans, root, path, level + 1, pslot +
-                                      1);
-                       if (wret)
-                               ret = wret;
+                       del_ptr(trans, root, path, level + 1, pslot + 1);
                        root_sub_used(root, right->len);
                        btrfs_free_tree_block(trans, root, right, 0, 1, 0);
-                       free_extent_buffer(right);
+                       free_extent_buffer_stale(right);
                        right = NULL;
                } else {
                        struct btrfs_disk_key right_key;
@@ -1035,7 +1060,11 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
                 * otherwise we would have pulled some pointers from the
                 * right
                 */
-               BUG_ON(!left);
+               if (!left) {
+                       ret = -EROFS;
+                       btrfs_std_error(root->fs_info, ret);
+                       goto enospc;
+               }
                wret = balance_node_right(trans, root, mid, left);
                if (wret < 0) {
                        ret = wret;
@@ -1051,12 +1080,10 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
        if (btrfs_header_nritems(mid) == 0) {
                clean_tree_block(trans, root, mid);
                btrfs_tree_unlock(mid);
-               wret = del_ptr(trans, root, path, level + 1, pslot);
-               if (wret)
-                       ret = wret;
+               del_ptr(trans, root, path, level + 1, pslot);
                root_sub_used(root, mid->len);
                btrfs_free_tree_block(trans, root, mid, 0, 1, 0);
-               free_extent_buffer(mid);
+               free_extent_buffer_stale(mid);
                mid = NULL;
        } else {
                /* update the parent key to reflect our changes */
@@ -1382,7 +1409,8 @@ static noinline int reada_for_balance(struct btrfs_root *root,
  * if lowest_unlock is 1, level 0 won't be unlocked
  */
 static noinline void unlock_up(struct btrfs_path *path, int level,
-                              int lowest_unlock)
+                              int lowest_unlock, int min_write_lock_level,
+                              int *write_lock_level)
 {
        int i;
        int skip_level = level;
@@ -1414,6 +1442,11 @@ static noinline void unlock_up(struct btrfs_path *path, int level,
                if (i >= lowest_unlock && i > skip_level && path->locks[i]) {
                        btrfs_tree_unlock_rw(t, path->locks[i]);
                        path->locks[i] = 0;
+                       if (write_lock_level &&
+                           i > min_write_lock_level &&
+                           i <= *write_lock_level) {
+                               *write_lock_level = i - 1;
+                       }
                }
        }
 }
@@ -1637,6 +1670,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
        /* everything at write_lock_level or lower must be write locked */
        int write_lock_level = 0;
        u8 lowest_level = 0;
+       int min_write_lock_level;
 
        lowest_level = p->lowest_level;
        WARN_ON(lowest_level && ins_len > 0);
@@ -1664,6 +1698,8 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
        if (cow && (p->keep_locks || p->lowest_level))
                write_lock_level = BTRFS_MAX_LEVEL;
 
+       min_write_lock_level = write_lock_level;
+
 again:
        /*
         * we try very hard to do read locks on the root
@@ -1795,7 +1831,8 @@ cow_done:
                                goto again;
                        }
 
-                       unlock_up(p, level, lowest_unlock);
+                       unlock_up(p, level, lowest_unlock,
+                                 min_write_lock_level, &write_lock_level);
 
                        if (level == lowest_level) {
                                if (dec)
@@ -1857,7 +1894,8 @@ cow_done:
                                }
                        }
                        if (!p->search_for_split)
-                               unlock_up(p, level, lowest_unlock);
+                               unlock_up(p, level, lowest_unlock,
+                                         min_write_lock_level, &write_lock_level);
                        goto done;
                }
        }
@@ -1881,15 +1919,12 @@ done:
  * fixing up pointers when a given leaf/node is not in slot 0 of the
  * higher levels
  *
- * If this fails to write a tree block, it returns -1, but continues
- * fixing up the blocks in ram so the tree is consistent.
  */
-static int fixup_low_keys(struct btrfs_trans_handle *trans,
-                         struct btrfs_root *root, struct btrfs_path *path,
-                         struct btrfs_disk_key *key, int level)
+static void fixup_low_keys(struct btrfs_trans_handle *trans,
+                          struct btrfs_root *root, struct btrfs_path *path,
+                          struct btrfs_disk_key *key, int level)
 {
        int i;
-       int ret = 0;
        struct extent_buffer *t;
 
        for (i = level; i < BTRFS_MAX_LEVEL; i++) {
@@ -1902,7 +1937,6 @@ static int fixup_low_keys(struct btrfs_trans_handle *trans,
                if (tslot != 0)
                        break;
        }
-       return ret;
 }
 
 /*
@@ -1911,9 +1945,9 @@ static int fixup_low_keys(struct btrfs_trans_handle *trans,
  * This function isn't completely safe. It's the caller's responsibility
  * that the new key won't break the order
  */
-int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
-                           struct btrfs_root *root, struct btrfs_path *path,
-                           struct btrfs_key *new_key)
+void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
+                            struct btrfs_root *root, struct btrfs_path *path,
+                            struct btrfs_key *new_key)
 {
        struct btrfs_disk_key disk_key;
        struct extent_buffer *eb;
@@ -1923,13 +1957,11 @@ int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
        slot = path->slots[0];
        if (slot > 0) {
                btrfs_item_key(eb, &disk_key, slot - 1);
-               if (comp_keys(&disk_key, new_key) >= 0)
-                       return -1;
+               BUG_ON(comp_keys(&disk_key, new_key) >= 0);
        }
        if (slot < btrfs_header_nritems(eb) - 1) {
                btrfs_item_key(eb, &disk_key, slot + 1);
-               if (comp_keys(&disk_key, new_key) <= 0)
-                       return -1;
+               BUG_ON(comp_keys(&disk_key, new_key) <= 0);
        }
 
        btrfs_cpu_key_to_disk(&disk_key, new_key);
@@ -1937,7 +1969,6 @@ int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(eb);
        if (slot == 0)
                fixup_low_keys(trans, root, path, &disk_key, 1);
-       return 0;
 }
 
 /*
@@ -2140,12 +2171,11 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
  *
  * slot and level indicate where you want the key to go, and
  * blocknr is the block the key points to.
- *
- * returns zero on success and < 0 on any error
  */
-static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root
-                     *root, struct btrfs_path *path, struct btrfs_disk_key
-                     *key, u64 bytenr, int slot, int level)
+static void insert_ptr(struct btrfs_trans_handle *trans,
+                      struct btrfs_root *root, struct btrfs_path *path,
+                      struct btrfs_disk_key *key, u64 bytenr,
+                      int slot, int level)
 {
        struct extent_buffer *lower;
        int nritems;
@@ -2155,8 +2185,7 @@ static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root
        lower = path->nodes[level];
        nritems = btrfs_header_nritems(lower);
        BUG_ON(slot > nritems);
-       if (nritems == BTRFS_NODEPTRS_PER_BLOCK(root))
-               BUG();
+       BUG_ON(nritems == BTRFS_NODEPTRS_PER_BLOCK(root));
        if (slot != nritems) {
                memmove_extent_buffer(lower,
                              btrfs_node_key_ptr_offset(slot + 1),
@@ -2169,7 +2198,6 @@ static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root
        btrfs_set_node_ptr_generation(lower, slot, trans->transid);
        btrfs_set_header_nritems(lower, nritems + 1);
        btrfs_mark_buffer_dirty(lower);
-       return 0;
 }
 
 /*
@@ -2190,7 +2218,6 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
        struct btrfs_disk_key disk_key;
        int mid;
        int ret;
-       int wret;
        u32 c_nritems;
 
        c = path->nodes[level];
@@ -2247,11 +2274,8 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(c);
        btrfs_mark_buffer_dirty(split);
 
-       wret = insert_ptr(trans, root, path, &disk_key, split->start,
-                         path->slots[level + 1] + 1,
-                         level + 1);
-       if (wret)
-               ret = wret;
+       insert_ptr(trans, root, path, &disk_key, split->start,
+                  path->slots[level + 1] + 1, level + 1);
 
        if (path->slots[level] >= mid) {
                path->slots[level] -= mid;
@@ -2320,6 +2344,7 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
 {
        struct extent_buffer *left = path->nodes[0];
        struct extent_buffer *upper = path->nodes[1];
+       struct btrfs_map_token token;
        struct btrfs_disk_key disk_key;
        int slot;
        u32 i;
@@ -2331,6 +2356,8 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
        u32 data_end;
        u32 this_item_size;
 
+       btrfs_init_map_token(&token);
+
        if (empty)
                nr = 0;
        else
@@ -2408,8 +2435,8 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
        push_space = BTRFS_LEAF_DATA_SIZE(root);
        for (i = 0; i < right_nritems; i++) {
                item = btrfs_item_nr(right, i);
-               push_space -= btrfs_item_size(right, item);
-               btrfs_set_item_offset(right, item, push_space);
+               push_space -= btrfs_token_item_size(right, item, &token);
+               btrfs_set_token_item_offset(right, item, push_space, &token);
        }
 
        left_nritems -= push_items;
@@ -2537,9 +2564,11 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
        u32 old_left_nritems;
        u32 nr;
        int ret = 0;
-       int wret;
        u32 this_item_size;
        u32 old_left_item_size;
+       struct btrfs_map_token token;
+
+       btrfs_init_map_token(&token);
 
        if (empty)
                nr = min(right_nritems, max_slot);
@@ -2600,9 +2629,10 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
 
                item = btrfs_item_nr(left, i);
 
-               ioff = btrfs_item_offset(left, item);
-               btrfs_set_item_offset(left, item,
-                     ioff - (BTRFS_LEAF_DATA_SIZE(root) - old_left_item_size));
+               ioff = btrfs_token_item_offset(left, item, &token);
+               btrfs_set_token_item_offset(left, item,
+                     ioff - (BTRFS_LEAF_DATA_SIZE(root) - old_left_item_size),
+                     &token);
        }
        btrfs_set_header_nritems(left, old_left_nritems + push_items);
 
@@ -2632,8 +2662,9 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
        for (i = 0; i < right_nritems; i++) {
                item = btrfs_item_nr(right, i);
 
-               push_space = push_space - btrfs_item_size(right, item);
-               btrfs_set_item_offset(right, item, push_space);
+               push_space = push_space - btrfs_token_item_size(right,
+                                                               item, &token);
+               btrfs_set_token_item_offset(right, item, push_space, &token);
        }
 
        btrfs_mark_buffer_dirty(left);
@@ -2643,9 +2674,7 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
                clean_tree_block(trans, root, right);
 
        btrfs_item_key(right, &disk_key, 0);
-       wret = fixup_low_keys(trans, root, path, &disk_key, 1);
-       if (wret)
-               ret = wret;
+       fixup_low_keys(trans, root, path, &disk_key, 1);
 
        /* then fixup the leaf pointer in the path */
        if (path->slots[0] < push_items) {
@@ -2716,7 +2745,8 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
                              path->nodes[1], slot - 1, &left);
        if (ret) {
                /* we hit -ENOSPC, but it isn't fatal here */
-               ret = 1;
+               if (ret == -ENOSPC)
+                       ret = 1;
                goto out;
        }
 
@@ -2738,22 +2768,21 @@ out:
 /*
  * split the path's leaf in two, making sure there is at least data_size
  * available for the resulting leaf level of the path.
- *
- * returns 0 if all went well and < 0 on failure.
  */
-static noinline int copy_for_split(struct btrfs_trans_handle *trans,
-                              struct btrfs_root *root,
-                              struct btrfs_path *path,
-                              struct extent_buffer *l,
-                              struct extent_buffer *right,
-                              int slot, int mid, int nritems)
+static noinline void copy_for_split(struct btrfs_trans_handle *trans,
+                                   struct btrfs_root *root,
+                                   struct btrfs_path *path,
+                                   struct extent_buffer *l,
+                                   struct extent_buffer *right,
+                                   int slot, int mid, int nritems)
 {
        int data_copy_size;
        int rt_data_off;
        int i;
-       int ret = 0;
-       int wret;
        struct btrfs_disk_key disk_key;
+       struct btrfs_map_token token;
+
+       btrfs_init_map_token(&token);
 
        nritems = nritems - mid;
        btrfs_set_header_nritems(right, nritems);
@@ -2775,17 +2804,15 @@ static noinline int copy_for_split(struct btrfs_trans_handle *trans,
                struct btrfs_item *item = btrfs_item_nr(right, i);
                u32 ioff;
 
-               ioff = btrfs_item_offset(right, item);
-               btrfs_set_item_offset(right, item, ioff + rt_data_off);
+               ioff = btrfs_token_item_offset(right, item, &token);
+               btrfs_set_token_item_offset(right, item,
+                                           ioff + rt_data_off, &token);
        }
 
        btrfs_set_header_nritems(l, mid);
-       ret = 0;
        btrfs_item_key(right, &disk_key, 0);
-       wret = insert_ptr(trans, root, path, &disk_key, right->start,
-                         path->slots[1] + 1, 1);
-       if (wret)
-               ret = wret;
+       insert_ptr(trans, root, path, &disk_key, right->start,
+                  path->slots[1] + 1, 1);
 
        btrfs_mark_buffer_dirty(right);
        btrfs_mark_buffer_dirty(l);
@@ -2803,8 +2830,6 @@ static noinline int copy_for_split(struct btrfs_trans_handle *trans,
        }
 
        BUG_ON(path->slots[0] < 0);
-
-       return ret;
 }
 
 /*
@@ -2993,12 +3018,8 @@ again:
        if (split == 0) {
                if (mid <= slot) {
                        btrfs_set_header_nritems(right, 0);
-                       wret = insert_ptr(trans, root, path,
-                                         &disk_key, right->start,
-                                         path->slots[1] + 1, 1);
-                       if (wret)
-                               ret = wret;
-
+                       insert_ptr(trans, root, path, &disk_key, right->start,
+                                  path->slots[1] + 1, 1);
                        btrfs_tree_unlock(path->nodes[0]);
                        free_extent_buffer(path->nodes[0]);
                        path->nodes[0] = right;
@@ -3006,29 +3027,21 @@ again:
                        path->slots[1] += 1;
                } else {
                        btrfs_set_header_nritems(right, 0);
-                       wret = insert_ptr(trans, root, path,
-                                         &disk_key,
-                                         right->start,
+                       insert_ptr(trans, root, path, &disk_key, right->start,
                                          path->slots[1], 1);
-                       if (wret)
-                               ret = wret;
                        btrfs_tree_unlock(path->nodes[0]);
                        free_extent_buffer(path->nodes[0]);
                        path->nodes[0] = right;
                        path->slots[0] = 0;
-                       if (path->slots[1] == 0) {
-                               wret = fixup_low_keys(trans, root,
-                                               path, &disk_key, 1);
-                               if (wret)
-                                       ret = wret;
-                       }
+                       if (path->slots[1] == 0)
+                               fixup_low_keys(trans, root, path,
+                                              &disk_key, 1);
                }
                btrfs_mark_buffer_dirty(right);
                return ret;
        }
 
-       ret = copy_for_split(trans, root, path, l, right, slot, mid, nritems);
-       BUG_ON(ret);
+       copy_for_split(trans, root, path, l, right, slot, mid, nritems);
 
        if (split == 2) {
                BUG_ON(num_doubles != 0);
@@ -3036,7 +3049,7 @@ again:
                goto again;
        }
 
-       return ret;
+       return 0;
 
 push_for_double:
        push_for_double_split(trans, root, path, data_size);
@@ -3238,11 +3251,9 @@ int btrfs_duplicate_item(struct btrfs_trans_handle *trans,
                return ret;
 
        path->slots[0]++;
-       ret = setup_items_for_insert(trans, root, path, new_key, &item_size,
-                                    item_size, item_size +
-                                    sizeof(struct btrfs_item), 1);
-       BUG_ON(ret);
-
+       setup_items_for_insert(trans, root, path, new_key, &item_size,
+                              item_size, item_size +
+                              sizeof(struct btrfs_item), 1);
        leaf = path->nodes[0];
        memcpy_extent_buffer(leaf,
                             btrfs_item_ptr_offset(leaf, path->slots[0]),
@@ -3257,10 +3268,10 @@ int btrfs_duplicate_item(struct btrfs_trans_handle *trans,
  * off the end of the item or if we shift the item to chop bytes off
  * the front.
  */
-int btrfs_truncate_item(struct btrfs_trans_handle *trans,
-                       struct btrfs_root *root,
-                       struct btrfs_path *path,
-                       u32 new_size, int from_end)
+void btrfs_truncate_item(struct btrfs_trans_handle *trans,
+                        struct btrfs_root *root,
+                        struct btrfs_path *path,
+                        u32 new_size, int from_end)
 {
        int slot;
        struct extent_buffer *leaf;
@@ -3271,13 +3282,16 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
        unsigned int old_size;
        unsigned int size_diff;
        int i;
+       struct btrfs_map_token token;
+
+       btrfs_init_map_token(&token);
 
        leaf = path->nodes[0];
        slot = path->slots[0];
 
        old_size = btrfs_item_size_nr(leaf, slot);
        if (old_size == new_size)
-               return 0;
+               return;
 
        nritems = btrfs_header_nritems(leaf);
        data_end = leaf_data_end(root, leaf);
@@ -3297,8 +3311,9 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
                u32 ioff;
                item = btrfs_item_nr(leaf, i);
 
-               ioff = btrfs_item_offset(leaf, item);
-               btrfs_set_item_offset(leaf, item, ioff + size_diff);
+               ioff = btrfs_token_item_offset(leaf, item, &token);
+               btrfs_set_token_item_offset(leaf, item,
+                                           ioff + size_diff, &token);
        }
 
        /* shift the data */
@@ -3350,15 +3365,14 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
                btrfs_print_leaf(root, leaf);
                BUG();
        }
-       return 0;
 }
 
 /*
  * make the item pointed to by the path bigger, data_size is the new size.
  */
-int btrfs_extend_item(struct btrfs_trans_handle *trans,
-                     struct btrfs_root *root, struct btrfs_path *path,
-                     u32 data_size)
+void btrfs_extend_item(struct btrfs_trans_handle *trans,
+                      struct btrfs_root *root, struct btrfs_path *path,
+                      u32 data_size)
 {
        int slot;
        struct extent_buffer *leaf;
@@ -3368,6 +3382,9 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans,
        unsigned int old_data;
        unsigned int old_size;
        int i;
+       struct btrfs_map_token token;
+
+       btrfs_init_map_token(&token);
 
        leaf = path->nodes[0];
 
@@ -3397,8 +3414,9 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans,
                u32 ioff;
                item = btrfs_item_nr(leaf, i);
 
-               ioff = btrfs_item_offset(leaf, item);
-               btrfs_set_item_offset(leaf, item, ioff - data_size);
+               ioff = btrfs_token_item_offset(leaf, item, &token);
+               btrfs_set_token_item_offset(leaf, item,
+                                           ioff - data_size, &token);
        }
 
        /* shift the data */
@@ -3416,7 +3434,6 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans,
                btrfs_print_leaf(root, leaf);
                BUG();
        }
-       return 0;
 }
 
 /*
@@ -3441,6 +3458,9 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
        unsigned int data_end;
        struct btrfs_disk_key disk_key;
        struct btrfs_key found_key;
+       struct btrfs_map_token token;
+
+       btrfs_init_map_token(&token);
 
        for (i = 0; i < nr; i++) {
                if (total_size + data_size[i] + sizeof(struct btrfs_item) >
@@ -3506,8 +3526,9 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
                        u32 ioff;
 
                        item = btrfs_item_nr(leaf, i);
-                       ioff = btrfs_item_offset(leaf, item);
-                       btrfs_set_item_offset(leaf, item, ioff - total_data);
+                       ioff = btrfs_token_item_offset(leaf, item, &token);
+                       btrfs_set_token_item_offset(leaf, item,
+                                                   ioff - total_data, &token);
                }
                /* shift the items */
                memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + nr),
@@ -3534,9 +3555,10 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
                btrfs_cpu_key_to_disk(&disk_key, cpu_key + i);
                btrfs_set_item_key(leaf, &disk_key, slot + i);
                item = btrfs_item_nr(leaf, slot + i);
-               btrfs_set_item_offset(leaf, item, data_end - data_size[i]);
+               btrfs_set_token_item_offset(leaf, item,
+                                           data_end - data_size[i], &token);
                data_end -= data_size[i];
-               btrfs_set_item_size(leaf, item, data_size[i]);
+               btrfs_set_token_item_size(leaf, item, data_size[i], &token);
        }
        btrfs_set_header_nritems(leaf, nritems + nr);
        btrfs_mark_buffer_dirty(leaf);
@@ -3544,7 +3566,7 @@ int btrfs_insert_some_items(struct btrfs_trans_handle *trans,
        ret = 0;
        if (slot == 0) {
                btrfs_cpu_key_to_disk(&disk_key, cpu_key);
-               ret = fixup_low_keys(trans, root, path, &disk_key, 1);
+               fixup_low_keys(trans, root, path, &disk_key, 1);
        }
 
        if (btrfs_leaf_free_space(root, leaf) < 0) {
@@ -3562,19 +3584,21 @@ out:
  * to save stack depth by doing the bulk of the work in a function
  * that doesn't call btrfs_search_slot
  */
-int setup_items_for_insert(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root, struct btrfs_path *path,
-                          struct btrfs_key *cpu_key, u32 *data_size,
-                          u32 total_data, u32 total_size, int nr)
+void setup_items_for_insert(struct btrfs_trans_handle *trans,
+                           struct btrfs_root *root, struct btrfs_path *path,
+                           struct btrfs_key *cpu_key, u32 *data_size,
+                           u32 total_data, u32 total_size, int nr)
 {
        struct btrfs_item *item;
        int i;
        u32 nritems;
        unsigned int data_end;
        struct btrfs_disk_key disk_key;
-       int ret;
        struct extent_buffer *leaf;
        int slot;
+       struct btrfs_map_token token;
+
+       btrfs_init_map_token(&token);
 
        leaf = path->nodes[0];
        slot = path->slots[0];
@@ -3606,8 +3630,9 @@ int setup_items_for_insert(struct btrfs_trans_handle *trans,
                        u32 ioff;
 
                        item = btrfs_item_nr(leaf, i);
-                       ioff = btrfs_item_offset(leaf, item);
-                       btrfs_set_item_offset(leaf, item, ioff - total_data);
+                       ioff = btrfs_token_item_offset(leaf, item, &token);
+                       btrfs_set_token_item_offset(leaf, item,
+                                                   ioff - total_data, &token);
                }
                /* shift the items */
                memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot + nr),
@@ -3626,17 +3651,17 @@ int setup_items_for_insert(struct btrfs_trans_handle *trans,
                btrfs_cpu_key_to_disk(&disk_key, cpu_key + i);
                btrfs_set_item_key(leaf, &disk_key, slot + i);
                item = btrfs_item_nr(leaf, slot + i);
-               btrfs_set_item_offset(leaf, item, data_end - data_size[i]);
+               btrfs_set_token_item_offset(leaf, item,
+                                           data_end - data_size[i], &token);
                data_end -= data_size[i];
-               btrfs_set_item_size(leaf, item, data_size[i]);
+               btrfs_set_token_item_size(leaf, item, data_size[i], &token);
        }
 
        btrfs_set_header_nritems(leaf, nritems + nr);
 
-       ret = 0;
        if (slot == 0) {
                btrfs_cpu_key_to_disk(&disk_key, cpu_key);
-               ret = fixup_low_keys(trans, root, path, &disk_key, 1);
+               fixup_low_keys(trans, root, path, &disk_key, 1);
        }
        btrfs_unlock_up_safe(path, 1);
        btrfs_mark_buffer_dirty(leaf);
@@ -3645,7 +3670,6 @@ int setup_items_for_insert(struct btrfs_trans_handle *trans,
                btrfs_print_leaf(root, leaf);
                BUG();
        }
-       return ret;
 }
 
 /*
@@ -3672,16 +3696,14 @@ int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
        if (ret == 0)
                return -EEXIST;
        if (ret < 0)
-               goto out;
+               return ret;
 
        slot = path->slots[0];
        BUG_ON(slot < 0);
 
-       ret = setup_items_for_insert(trans, root, path, cpu_key, data_size,
+       setup_items_for_insert(trans, root, path, cpu_key, data_size,
                               total_data, total_size, nr);
-
-out:
-       return ret;
+       return 0;
 }
 
 /*
@@ -3717,13 +3739,11 @@ int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
  * the tree should have been previously balanced so the deletion does not
  * empty a node.
  */
-static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-                  struct btrfs_path *path, int level, int slot)
+static void del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+                   struct btrfs_path *path, int level, int slot)
 {
        struct extent_buffer *parent = path->nodes[level];
        u32 nritems;
-       int ret = 0;
-       int wret;
 
        nritems = btrfs_header_nritems(parent);
        if (slot != nritems - 1) {
@@ -3743,12 +3763,9 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                struct btrfs_disk_key disk_key;
 
                btrfs_node_key(parent, &disk_key, 0);
-               wret = fixup_low_keys(trans, root, path, &disk_key, level + 1);
-               if (wret)
-                       ret = wret;
+               fixup_low_keys(trans, root, path, &disk_key, level + 1);
        }
        btrfs_mark_buffer_dirty(parent);
-       return ret;
 }
 
 /*
@@ -3761,17 +3778,13 @@ static int del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
  * The path must have already been setup for deleting the leaf, including
  * all the proper balancing.  path->nodes[1] must be locked.
  */
-static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans,
-                                  struct btrfs_root *root,
-                                  struct btrfs_path *path,
-                                  struct extent_buffer *leaf)
+static noinline void btrfs_del_leaf(struct btrfs_trans_handle *trans,
+                                   struct btrfs_root *root,
+                                   struct btrfs_path *path,
+                                   struct extent_buffer *leaf)
 {
-       int ret;
-
        WARN_ON(btrfs_header_generation(leaf) != trans->transid);
-       ret = del_ptr(trans, root, path, 1, path->slots[1]);
-       if (ret)
-               return ret;
+       del_ptr(trans, root, path, 1, path->slots[1]);
 
        /*
         * btrfs_free_extent is expensive, we want to make sure we
@@ -3781,8 +3794,9 @@ static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans,
 
        root_sub_used(root, leaf->len);
 
+       extent_buffer_get(leaf);
        btrfs_free_tree_block(trans, root, leaf, 0, 1, 0);
-       return 0;
+       free_extent_buffer_stale(leaf);
 }
 /*
  * delete the item at the leaf level in path.  If that empties
@@ -3799,6 +3813,9 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
        int wret;
        int i;
        u32 nritems;
+       struct btrfs_map_token token;
+
+       btrfs_init_map_token(&token);
 
        leaf = path->nodes[0];
        last_off = btrfs_item_offset_nr(leaf, slot + nr - 1);
@@ -3820,8 +3837,9 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                        u32 ioff;
 
                        item = btrfs_item_nr(leaf, i);
-                       ioff = btrfs_item_offset(leaf, item);
-                       btrfs_set_item_offset(leaf, item, ioff + dsize);
+                       ioff = btrfs_token_item_offset(leaf, item, &token);
+                       btrfs_set_token_item_offset(leaf, item,
+                                                   ioff + dsize, &token);
                }
 
                memmove_extent_buffer(leaf, btrfs_item_nr_offset(slot),
@@ -3839,8 +3857,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                } else {
                        btrfs_set_path_blocking(path);
                        clean_tree_block(trans, root, leaf);
-                       ret = btrfs_del_leaf(trans, root, path, leaf);
-                       BUG_ON(ret);
+                       btrfs_del_leaf(trans, root, path, leaf);
                }
        } else {
                int used = leaf_space_used(leaf, 0, nritems);
@@ -3848,10 +3865,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                        struct btrfs_disk_key disk_key;
 
                        btrfs_item_key(leaf, &disk_key, 0);
-                       wret = fixup_low_keys(trans, root, path,
-                                             &disk_key, 1);
-                       if (wret)
-                               ret = wret;
+                       fixup_low_keys(trans, root, path, &disk_key, 1);
                }
 
                /* delete the leaf if it is mostly empty */
@@ -3879,9 +3893,9 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
 
                        if (btrfs_header_nritems(leaf) == 0) {
                                path->slots[1] = slot;
-                               ret = btrfs_del_leaf(trans, root, path, leaf);
-                               BUG_ON(ret);
+                               btrfs_del_leaf(trans, root, path, leaf);
                                free_extent_buffer(leaf);
+                               ret = 0;
                        } else {
                                /* if we're still in the path, make sure
                                 * we're dirty.  Otherwise, one of the
@@ -4059,18 +4073,18 @@ find_next_key:
                path->slots[level] = slot;
                if (level == path->lowest_level) {
                        ret = 0;
-                       unlock_up(path, level, 1);
+                       unlock_up(path, level, 1, 0, NULL);
                        goto out;
                }
                btrfs_set_path_blocking(path);
                cur = read_node_slot(root, cur, slot);
-               BUG_ON(!cur);
+               BUG_ON(!cur); /* -ENOMEM */
 
                btrfs_tree_read_lock(cur);
 
                path->locks[level - 1] = BTRFS_READ_LOCK;
                path->nodes[level - 1] = cur;
-               unlock_up(path, level, 1);
+               unlock_up(path, level, 1, 0, NULL);
                btrfs_clear_path_blocking(path, NULL, 0);
        }
 out:
@@ -4306,7 +4320,7 @@ again:
        }
        ret = 0;
 done:
-       unlock_up(path, 0, 1);
+       unlock_up(path, 0, 1, 0, NULL);
        path->leave_spinning = old_spinning;
        if (!old_spinning)
                btrfs_set_path_blocking(path);
index 80b6486fd5e647b6663687cce5aa66b764de3ed0..5b8ef8eb35218e0f595ee93069fc1ccab49c6b1e 100644 (file)
@@ -48,6 +48,8 @@ struct btrfs_ordered_sum;
 
 #define BTRFS_MAGIC "_BHRfS_M"
 
+#define BTRFS_MAX_MIRRORS 2
+
 #define BTRFS_MAX_LEVEL 8
 
 #define BTRFS_COMPAT_EXTENT_TREE_V0
@@ -137,6 +139,12 @@ struct btrfs_ordered_sum;
 
 #define BTRFS_EMPTY_SUBVOL_DIR_OBJECTID 2
 
+/*
+ * the max metadata block size.  This limit is somewhat artificial,
+ * but the memmove costs go through the roof for larger blocks.
+ */
+#define BTRFS_MAX_METADATA_BLOCKSIZE 65536
+
 /*
  * we can actually store much bigger names, but lets not confuse the rest
  * of linux
@@ -461,6 +469,19 @@ struct btrfs_super_block {
 #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL  (1ULL << 1)
 #define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS    (1ULL << 2)
 #define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO    (1ULL << 3)
+/*
+ * some patches floated around with a second compression method
+ * lets save that incompat here for when they do get in
+ * Note we don't actually support it, we're just reserving the
+ * number
+ */
+#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZOv2  (1ULL << 4)
+
+/*
+ * older kernels tried to do bigger metadata blocks, but the
+ * code was pretty buggy.  Lets not let them try anymore.
+ */
+#define BTRFS_FEATURE_INCOMPAT_BIG_METADATA    (1ULL << 5)
 
 #define BTRFS_FEATURE_COMPAT_SUPP              0ULL
 #define BTRFS_FEATURE_COMPAT_RO_SUPP           0ULL
@@ -468,6 +489,7 @@ struct btrfs_super_block {
        (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF |         \
         BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL |        \
         BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS |          \
+        BTRFS_FEATURE_INCOMPAT_BIG_METADATA |          \
         BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO)
 
 /*
@@ -829,6 +851,21 @@ struct btrfs_csum_item {
  */
 #define BTRFS_AVAIL_ALLOC_BIT_SINGLE   (1ULL << 48)
 
+#define BTRFS_EXTENDED_PROFILE_MASK    (BTRFS_BLOCK_GROUP_PROFILE_MASK | \
+                                        BTRFS_AVAIL_ALLOC_BIT_SINGLE)
+
+static inline u64 chunk_to_extended(u64 flags)
+{
+       if ((flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0)
+               flags |= BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+
+       return flags;
+}
+static inline u64 extended_to_chunk(u64 flags)
+{
+       return flags & ~BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+}
+
 struct btrfs_block_group_item {
        __le64 used;
        __le64 chunk_objectid;
@@ -1503,6 +1540,7 @@ struct btrfs_ioctl_defrag_range_args {
 #define BTRFS_MOUNT_SKIP_BALANCE       (1 << 19)
 #define BTRFS_MOUNT_CHECK_INTEGRITY    (1 << 20)
 #define BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA (1 << 21)
+#define BTRFS_MOUNT_PANIC_ON_FATAL_ERROR       (1 << 22)
 
 #define btrfs_clear_opt(o, opt)                ((o) &= ~BTRFS_MOUNT_##opt)
 #define btrfs_set_opt(o, opt)          ((o) |= BTRFS_MOUNT_##opt)
@@ -1526,6 +1564,17 @@ struct btrfs_ioctl_defrag_range_args {
 
 #define BTRFS_INODE_ROOT_ITEM_INIT     (1 << 31)
 
+struct btrfs_map_token {
+       struct extent_buffer *eb;
+       char *kaddr;
+       unsigned long offset;
+};
+
+static inline void btrfs_init_map_token (struct btrfs_map_token *token)
+{
+       memset(token, 0, sizeof(*token));
+}
+
 /* some macros to generate set/get funcs for the struct fields.  This
  * assumes there is a lefoo_to_cpu for every type, so lets make a simple
  * one for u8:
@@ -1549,20 +1598,22 @@ struct btrfs_ioctl_defrag_range_args {
 #ifndef BTRFS_SETGET_FUNCS
 #define BTRFS_SETGET_FUNCS(name, type, member, bits)                   \
 u##bits btrfs_##name(struct extent_buffer *eb, type *s);               \
+u##bits btrfs_token_##name(struct extent_buffer *eb, type *s, struct btrfs_map_token *token);          \
+void btrfs_set_token_##name(struct extent_buffer *eb, type *s, u##bits val, struct btrfs_map_token *token);\
 void btrfs_set_##name(struct extent_buffer *eb, type *s, u##bits val);
 #endif
 
 #define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits)            \
 static inline u##bits btrfs_##name(struct extent_buffer *eb)           \
 {                                                                      \
-       type *p = page_address(eb->first_page);                         \
+       type *p = page_address(eb->pages[0]);                           \
        u##bits res = le##bits##_to_cpu(p->member);                     \
        return res;                                                     \
 }                                                                      \
 static inline void btrfs_set_##name(struct extent_buffer *eb,          \
                                    u##bits val)                        \
 {                                                                      \
-       type *p = page_address(eb->first_page);                         \
+       type *p = page_address(eb->pages[0]);                           \
        p->member = cpu_to_le##bits(val);                               \
 }
 
@@ -2466,8 +2517,7 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
                                  struct btrfs_root *root,
                                  u64 num_bytes, u64 min_alloc_size,
                                  u64 empty_size, u64 hint_byte,
-                                 u64 search_end, struct btrfs_key *ins,
-                                 u64 data);
+                                 struct btrfs_key *ins, u64 data);
 int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                  struct extent_buffer *buf, int full_backref, int for_cow);
 int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
@@ -2484,8 +2534,8 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans,
 int btrfs_free_reserved_extent(struct btrfs_root *root, u64 start, u64 len);
 int btrfs_free_and_pin_reserved_extent(struct btrfs_root *root,
                                       u64 start, u64 len);
-int btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
-                               struct btrfs_root *root);
+void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root);
 int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
                               struct btrfs_root *root);
 int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
@@ -2548,8 +2598,8 @@ void btrfs_block_rsv_release(struct btrfs_root *root,
                             u64 num_bytes);
 int btrfs_set_block_group_ro(struct btrfs_root *root,
                             struct btrfs_block_group_cache *cache);
-int btrfs_set_block_group_rw(struct btrfs_root *root,
-                            struct btrfs_block_group_cache *cache);
+void btrfs_set_block_group_rw(struct btrfs_root *root,
+                             struct btrfs_block_group_cache *cache);
 void btrfs_put_block_group_cache(struct btrfs_fs_info *info);
 u64 btrfs_account_ro_block_groups_free_space(struct btrfs_space_info *sinfo);
 int btrfs_error_unpin_extent_range(struct btrfs_root *root,
@@ -2568,9 +2618,9 @@ int btrfs_comp_cpu_keys(struct btrfs_key *k1, struct btrfs_key *k2);
 int btrfs_previous_item(struct btrfs_root *root,
                        struct btrfs_path *path, u64 min_objectid,
                        int type);
-int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
-                           struct btrfs_root *root, struct btrfs_path *path,
-                           struct btrfs_key *new_key);
+void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
+                            struct btrfs_root *root, struct btrfs_path *path,
+                            struct btrfs_key *new_key);
 struct extent_buffer *btrfs_root_node(struct btrfs_root *root);
 struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root);
 int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path,
@@ -2590,12 +2640,13 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
                      struct extent_buffer **cow_ret, u64 new_root_objectid);
 int btrfs_block_can_be_shared(struct btrfs_root *root,
                              struct extent_buffer *buf);
-int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root
-                     *root, struct btrfs_path *path, u32 data_size);
-int btrfs_truncate_item(struct btrfs_trans_handle *trans,
-                       struct btrfs_root *root,
-                       struct btrfs_path *path,
-                       u32 new_size, int from_end);
+void btrfs_extend_item(struct btrfs_trans_handle *trans,
+                      struct btrfs_root *root, struct btrfs_path *path,
+                      u32 data_size);
+void btrfs_truncate_item(struct btrfs_trans_handle *trans,
+                        struct btrfs_root *root,
+                        struct btrfs_path *path,
+                        u32 new_size, int from_end);
 int btrfs_split_item(struct btrfs_trans_handle *trans,
                     struct btrfs_root *root,
                     struct btrfs_path *path,
@@ -2629,10 +2680,10 @@ static inline int btrfs_del_item(struct btrfs_trans_handle *trans,
        return btrfs_del_items(trans, root, path, path->slots[0], 1);
 }
 
-int setup_items_for_insert(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root, struct btrfs_path *path,
-                          struct btrfs_key *cpu_key, u32 *data_size,
-                          u32 total_data, u32 total_size, int nr);
+void setup_items_for_insert(struct btrfs_trans_handle *trans,
+                           struct btrfs_root *root, struct btrfs_path *path,
+                           struct btrfs_key *cpu_key, u32 *data_size,
+                           u32 total_data, u32 total_size, int nr);
 int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
                      *root, struct btrfs_key *key, void *data, u32 data_size);
 int btrfs_insert_empty_items(struct btrfs_trans_handle *trans,
@@ -2659,9 +2710,9 @@ static inline int btrfs_next_item(struct btrfs_root *root, struct btrfs_path *p)
 }
 int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
 int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf);
-void btrfs_drop_snapshot(struct btrfs_root *root,
-                        struct btrfs_block_rsv *block_rsv, int update_ref,
-                        int for_reloc);
+int __must_check btrfs_drop_snapshot(struct btrfs_root *root,
+                                    struct btrfs_block_rsv *block_rsv,
+                                    int update_ref, int for_reloc);
 int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
                        struct btrfs_root *root,
                        struct extent_buffer *node,
@@ -2687,24 +2738,6 @@ static inline void free_fs_info(struct btrfs_fs_info *fs_info)
        kfree(fs_info->super_for_commit);
        kfree(fs_info);
 }
-/**
- * profile_is_valid - tests whether a given profile is valid and reduced
- * @flags: profile to validate
- * @extended: if true @flags is treated as an extended profile
- */
-static inline int profile_is_valid(u64 flags, int extended)
-{
-       u64 mask = ~BTRFS_BLOCK_GROUP_PROFILE_MASK;
-
-       flags &= ~BTRFS_BLOCK_GROUP_TYPE_MASK;
-       if (extended)
-               mask &= ~BTRFS_AVAIL_ALLOC_BIT_SINGLE;
-
-       if (flags & mask)
-               return 0;
-       /* true if zero or exactly one bit set */
-       return (flags & (~flags + 1)) == flags;
-}
 
 /* root-item.c */
 int btrfs_find_root_ref(struct btrfs_root *tree_root,
@@ -2723,9 +2756,10 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
 int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
                      *root, struct btrfs_key *key, struct btrfs_root_item
                      *item);
-int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
-                     *root, struct btrfs_key *key, struct btrfs_root_item
-                     *item);
+int __must_check btrfs_update_root(struct btrfs_trans_handle *trans,
+                                  struct btrfs_root *root,
+                                  struct btrfs_key *key,
+                                  struct btrfs_root_item *item);
 int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
                         btrfs_root_item *item, struct btrfs_key *key);
 int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
@@ -2909,7 +2943,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root);
 void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root);
 int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size);
-int btrfs_invalidate_inodes(struct btrfs_root *root);
+void btrfs_invalidate_inodes(struct btrfs_root *root);
 void btrfs_add_delayed_iput(struct inode *inode);
 void btrfs_run_delayed_iputs(struct btrfs_root *root);
 int btrfs_prealloc_file_range(struct inode *inode, int mode,
@@ -2961,13 +2995,41 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
 /* super.c */
 int btrfs_parse_options(struct btrfs_root *root, char *options);
 int btrfs_sync_fs(struct super_block *sb, int wait);
+void btrfs_printk(struct btrfs_fs_info *fs_info, const char *fmt, ...);
 void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function,
-                    unsigned int line, int errno);
+                    unsigned int line, int errno, const char *fmt, ...);
+
+void __btrfs_abort_transaction(struct btrfs_trans_handle *trans,
+                              struct btrfs_root *root, const char *function,
+                              unsigned int line, int errno);
+
+#define btrfs_abort_transaction(trans, root, errno)            \
+do {                                                           \
+       __btrfs_abort_transaction(trans, root, __func__,        \
+                                 __LINE__, errno);             \
+} while (0)
 
 #define btrfs_std_error(fs_info, errno)                                \
 do {                                                           \
        if ((errno))                                            \
-               __btrfs_std_error((fs_info), __func__, __LINE__, (errno));\
+               __btrfs_std_error((fs_info), __func__,          \
+                                  __LINE__, (errno), NULL);    \
+} while (0)
+
+#define btrfs_error(fs_info, errno, fmt, args...)              \
+do {                                                           \
+       __btrfs_std_error((fs_info), __func__, __LINE__,        \
+                         (errno), fmt, ##args);                \
+} while (0)
+
+void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function,
+                  unsigned int line, int errno, const char *fmt, ...);
+
+#define btrfs_panic(fs_info, errno, fmt, args...)                      \
+do {                                                                   \
+       struct btrfs_fs_info *_i = (fs_info);                           \
+       __btrfs_panic(_i, __func__, __LINE__, errno, fmt, ##args);      \
+       BUG_ON(!(_i->mount_opt & BTRFS_MOUNT_PANIC_ON_FATAL_ERROR));    \
 } while (0)
 
 /* acl.c */
@@ -3003,16 +3065,17 @@ void btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
 void btrfs_reloc_pre_snapshot(struct btrfs_trans_handle *trans,
                              struct btrfs_pending_snapshot *pending,
                              u64 *bytes_to_reserve);
-void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
+int btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
                              struct btrfs_pending_snapshot *pending);
 
 /* scrub.c */
 int btrfs_scrub_dev(struct btrfs_root *root, u64 devid, u64 start, u64 end,
                    struct btrfs_scrub_progress *progress, int readonly);
-int btrfs_scrub_pause(struct btrfs_root *root);
-int btrfs_scrub_pause_super(struct btrfs_root *root);
-int btrfs_scrub_continue(struct btrfs_root *root);
-int btrfs_scrub_continue_super(struct btrfs_root *root);
+void btrfs_scrub_pause(struct btrfs_root *root);
+void btrfs_scrub_pause_super(struct btrfs_root *root);
+void btrfs_scrub_continue(struct btrfs_root *root);
+void btrfs_scrub_continue_super(struct btrfs_root *root);
+int __btrfs_scrub_cancel(struct btrfs_fs_info *info);
 int btrfs_scrub_cancel(struct btrfs_root *root);
 int btrfs_scrub_cancel_dev(struct btrfs_root *root, struct btrfs_device *dev);
 int btrfs_scrub_cancel_devid(struct btrfs_root *root, u64 devid);
index fe4cd0f1cef188b8cf584c67c8ab0f58ffb62cbb..03e3748d84d02407c19c6d46648667a56f13ba3e 100644 (file)
@@ -115,6 +115,7 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(struct inode *inode)
        return NULL;
 }
 
+/* Will return either the node or PTR_ERR(-ENOMEM) */
 static struct btrfs_delayed_node *btrfs_get_or_create_delayed_node(
                                                        struct inode *inode)
 {
@@ -836,10 +837,8 @@ static int btrfs_batch_insert_items(struct btrfs_trans_handle *trans,
        btrfs_clear_path_blocking(path, NULL, 0);
 
        /* insert the keys of the items */
-       ret = setup_items_for_insert(trans, root, path, keys, data_size,
-                                    total_data_size, total_size, nitems);
-       if (ret)
-               goto error;
+       setup_items_for_insert(trans, root, path, keys, data_size,
+                              total_data_size, total_size, nitems);
 
        /* insert the dir index items */
        slot = path->slots[0];
@@ -1108,16 +1107,25 @@ static int btrfs_update_delayed_inode(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-/* Called when committing the transaction. */
+/*
+ * Called when committing the transaction.
+ * Returns 0 on success.
+ * Returns < 0 on error and returns with an aborted transaction with any
+ * outstanding delayed items cleaned up.
+ */
 int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
                            struct btrfs_root *root)
 {
+       struct btrfs_root *curr_root = root;
        struct btrfs_delayed_root *delayed_root;
        struct btrfs_delayed_node *curr_node, *prev_node;
        struct btrfs_path *path;
        struct btrfs_block_rsv *block_rsv;
        int ret = 0;
 
+       if (trans->aborted)
+               return -EIO;
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -1130,17 +1138,18 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
 
        curr_node = btrfs_first_delayed_node(delayed_root);
        while (curr_node) {
-               root = curr_node->root;
-               ret = btrfs_insert_delayed_items(trans, path, root,
+               curr_root = curr_node->root;
+               ret = btrfs_insert_delayed_items(trans, path, curr_root,
                                                 curr_node);
                if (!ret)
-                       ret = btrfs_delete_delayed_items(trans, path, root,
-                                                        curr_node);
+                       ret = btrfs_delete_delayed_items(trans, path,
+                                               curr_root, curr_node);
                if (!ret)
-                       ret = btrfs_update_delayed_inode(trans, root, path,
-                                                        curr_node);
+                       ret = btrfs_update_delayed_inode(trans, curr_root,
+                                               path, curr_node);
                if (ret) {
                        btrfs_release_delayed_node(curr_node);
+                       btrfs_abort_transaction(trans, root, ret);
                        break;
                }
 
@@ -1151,6 +1160,7 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
 
        btrfs_free_path(path);
        trans->block_rsv = block_rsv;
+
        return ret;
 }
 
@@ -1371,6 +1381,7 @@ void btrfs_balance_delayed_items(struct btrfs_root *root)
        btrfs_wq_run_delayed_node(delayed_root, root, 0);
 }
 
+/* Will return 0 or -ENOMEM */
 int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
                                   struct btrfs_root *root, const char *name,
                                   int name_len, struct inode *dir,
index 66e4f29505a33dbecd45b5d6a80e878c87818bc0..69f22e3ab3bc307974b5cae14f99310a498b54cf 100644 (file)
@@ -420,7 +420,7 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
  * this does all the dirty work in terms of maintaining the correct
  * overall modification count.
  */
-static noinline int add_delayed_ref_head(struct btrfs_fs_info *fs_info,
+static noinline void add_delayed_ref_head(struct btrfs_fs_info *fs_info,
                                        struct btrfs_trans_handle *trans,
                                        struct btrfs_delayed_ref_node *ref,
                                        u64 bytenr, u64 num_bytes,
@@ -487,20 +487,19 @@ static noinline int add_delayed_ref_head(struct btrfs_fs_info *fs_info,
                 * we've updated the existing ref, free the newly
                 * allocated ref
                 */
-               kfree(ref);
+               kfree(head_ref);
        } else {
                delayed_refs->num_heads++;
                delayed_refs->num_heads_ready++;
                delayed_refs->num_entries++;
                trans->delayed_ref_updates++;
        }
-       return 0;
 }
 
 /*
  * helper to insert a delayed tree ref into the rbtree.
  */
-static noinline int add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
+static noinline void add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
                                         struct btrfs_trans_handle *trans,
                                         struct btrfs_delayed_ref_node *ref,
                                         u64 bytenr, u64 num_bytes, u64 parent,
@@ -549,18 +548,17 @@ static noinline int add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
                 * we've updated the existing ref, free the newly
                 * allocated ref
                 */
-               kfree(ref);
+               kfree(full_ref);
        } else {
                delayed_refs->num_entries++;
                trans->delayed_ref_updates++;
        }
-       return 0;
 }
 
 /*
  * helper to insert a delayed data ref into the rbtree.
  */
-static noinline int add_delayed_data_ref(struct btrfs_fs_info *fs_info,
+static noinline void add_delayed_data_ref(struct btrfs_fs_info *fs_info,
                                         struct btrfs_trans_handle *trans,
                                         struct btrfs_delayed_ref_node *ref,
                                         u64 bytenr, u64 num_bytes, u64 parent,
@@ -611,12 +609,11 @@ static noinline int add_delayed_data_ref(struct btrfs_fs_info *fs_info,
                 * we've updated the existing ref, free the newly
                 * allocated ref
                 */
-               kfree(ref);
+               kfree(full_ref);
        } else {
                delayed_refs->num_entries++;
                trans->delayed_ref_updates++;
        }
-       return 0;
 }
 
 /*
@@ -634,7 +631,6 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
        struct btrfs_delayed_tree_ref *ref;
        struct btrfs_delayed_ref_head *head_ref;
        struct btrfs_delayed_ref_root *delayed_refs;
-       int ret;
 
        BUG_ON(extent_op && extent_op->is_data);
        ref = kmalloc(sizeof(*ref), GFP_NOFS);
@@ -656,14 +652,12 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
         * insert both the head node and the new ref without dropping
         * the spin lock
         */
-       ret = add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
+       add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
                                   num_bytes, action, 0);
-       BUG_ON(ret);
 
-       ret = add_delayed_tree_ref(fs_info, trans, &ref->node, bytenr,
+       add_delayed_tree_ref(fs_info, trans, &ref->node, bytenr,
                                   num_bytes, parent, ref_root, level, action,
                                   for_cow);
-       BUG_ON(ret);
        if (!need_ref_seq(for_cow, ref_root) &&
            waitqueue_active(&delayed_refs->seq_wait))
                wake_up(&delayed_refs->seq_wait);
@@ -685,7 +679,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
        struct btrfs_delayed_data_ref *ref;
        struct btrfs_delayed_ref_head *head_ref;
        struct btrfs_delayed_ref_root *delayed_refs;
-       int ret;
 
        BUG_ON(extent_op && !extent_op->is_data);
        ref = kmalloc(sizeof(*ref), GFP_NOFS);
@@ -707,14 +700,12 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
         * insert both the head node and the new ref without dropping
         * the spin lock
         */
-       ret = add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
+       add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
                                   num_bytes, action, 1);
-       BUG_ON(ret);
 
-       ret = add_delayed_data_ref(fs_info, trans, &ref->node, bytenr,
+       add_delayed_data_ref(fs_info, trans, &ref->node, bytenr,
                                   num_bytes, parent, ref_root, owner, offset,
                                   action, for_cow);
-       BUG_ON(ret);
        if (!need_ref_seq(for_cow, ref_root) &&
            waitqueue_active(&delayed_refs->seq_wait))
                wake_up(&delayed_refs->seq_wait);
@@ -729,7 +720,6 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info,
 {
        struct btrfs_delayed_ref_head *head_ref;
        struct btrfs_delayed_ref_root *delayed_refs;
-       int ret;
 
        head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
        if (!head_ref)
@@ -740,10 +730,9 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info,
        delayed_refs = &trans->transaction->delayed_refs;
        spin_lock(&delayed_refs->lock);
 
-       ret = add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
+       add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr,
                                   num_bytes, BTRFS_UPDATE_DELAYED_HEAD,
                                   extent_op->is_data);
-       BUG_ON(ret);
 
        if (waitqueue_active(&delayed_refs->seq_wait))
                wake_up(&delayed_refs->seq_wait);
index 31d84e78129b34adaac7a1ec25368e8c3212403d..c1a074d0696ff897258c576c127af7fd8d513846 100644 (file)
@@ -49,9 +49,8 @@ static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
                di = btrfs_match_dir_item_name(root, path, name, name_len);
                if (di)
                        return ERR_PTR(-EEXIST);
-               ret = btrfs_extend_item(trans, root, path, data_size);
-       }
-       if (ret < 0)
+               btrfs_extend_item(trans, root, path, data_size);
+       } else if (ret < 0)
                return ERR_PTR(ret);
        WARN_ON(ret > 0);
        leaf = path->nodes[0];
@@ -116,6 +115,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
  * 'location' is the key to stuff into the directory item, 'type' is the
  * type of the inode we're pointing to, and 'index' is the sequence number
  * to use for the second index (if one is created).
+ * Will return 0 or -ENOMEM
  */
 int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
                          *root, const char *name, int name_len,
@@ -383,8 +383,8 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
                start = btrfs_item_ptr_offset(leaf, path->slots[0]);
                memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
                        item_len - (ptr + sub_item_len - start));
-               ret = btrfs_truncate_item(trans, root, path,
-                                         item_len - sub_item_len, 1);
+               btrfs_truncate_item(trans, root, path,
+                                   item_len - sub_item_len, 1);
        }
        return ret;
 }
index 534266fe505f25cf4a89e25036ff7994cb8c28d5..20196f41120698f5d7efd7c6be5c56415bd44651 100644 (file)
 static struct extent_io_ops btree_extent_io_ops;
 static void end_workqueue_fn(struct btrfs_work *work);
 static void free_fs_root(struct btrfs_root *root);
-static void btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
+static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
                                    int read_only);
-static int btrfs_destroy_ordered_operations(struct btrfs_root *root);
-static int btrfs_destroy_ordered_extents(struct btrfs_root *root);
+static void btrfs_destroy_ordered_operations(struct btrfs_root *root);
+static void btrfs_destroy_ordered_extents(struct btrfs_root *root);
 static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
                                      struct btrfs_root *root);
-static int btrfs_destroy_pending_snapshots(struct btrfs_transaction *t);
-static int btrfs_destroy_delalloc_inodes(struct btrfs_root *root);
+static void btrfs_destroy_pending_snapshots(struct btrfs_transaction *t);
+static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root);
 static int btrfs_destroy_marked_extents(struct btrfs_root *root,
                                        struct extent_io_tree *dirty_pages,
                                        int mark);
 static int btrfs_destroy_pinned_extent(struct btrfs_root *root,
                                       struct extent_io_tree *pinned_extents);
-static int btrfs_cleanup_transaction(struct btrfs_root *root);
 
 /*
  * end_io_wq structs are used to do processing in task context when an IO is
@@ -99,6 +98,7 @@ struct async_submit_bio {
         */
        u64 bio_offset;
        struct btrfs_work work;
+       int error;
 };
 
 /*
@@ -332,8 +332,8 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
                return 0;
 
        lock_extent_bits(io_tree, eb->start, eb->start + eb->len - 1,
-                        0, &cached_state, GFP_NOFS);
-       if (extent_buffer_uptodate(io_tree, eb, cached_state) &&
+                        0, &cached_state);
+       if (extent_buffer_uptodate(eb) &&
            btrfs_header_generation(eb) == parent_transid) {
                ret = 0;
                goto out;
@@ -344,7 +344,7 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
                       (unsigned long long)parent_transid,
                       (unsigned long long)btrfs_header_generation(eb));
        ret = 1;
-       clear_extent_buffer_uptodate(io_tree, eb, &cached_state);
+       clear_extent_buffer_uptodate(eb);
 out:
        unlock_extent_cached(io_tree, eb->start, eb->start + eb->len - 1,
                             &cached_state, GFP_NOFS);
@@ -360,9 +360,11 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root,
                                          u64 start, u64 parent_transid)
 {
        struct extent_io_tree *io_tree;
+       int failed = 0;
        int ret;
        int num_copies = 0;
        int mirror_num = 0;
+       int failed_mirror = 0;
 
        clear_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
        io_tree = &BTRFS_I(root->fs_info->btree_inode)->io_tree;
@@ -370,9 +372,8 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root,
                ret = read_extent_buffer_pages(io_tree, eb, start,
                                               WAIT_COMPLETE,
                                               btree_get_extent, mirror_num);
-               if (!ret &&
-                   !verify_parent_transid(io_tree, eb, parent_transid))
-                       return ret;
+               if (!ret && !verify_parent_transid(io_tree, eb, parent_transid))
+                       break;
 
                /*
                 * This buffer's crc is fine, but its contents are corrupted, so
@@ -380,18 +381,31 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root,
                 * any less wrong.
                 */
                if (test_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags))
-                       return ret;
+                       break;
+
+               if (!failed_mirror) {
+                       failed = 1;
+                       printk(KERN_ERR "failed mirror was %d\n", eb->failed_mirror);
+                       failed_mirror = eb->failed_mirror;
+               }
 
                num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
                                              eb->start, eb->len);
                if (num_copies == 1)
-                       return ret;
+                       break;
 
                mirror_num++;
+               if (mirror_num == failed_mirror)
+                       mirror_num++;
+
                if (mirror_num > num_copies)
-                       return ret;
+                       break;
        }
-       return -EIO;
+
+       if (failed && !ret)
+               repair_eb_io_failure(root, eb, failed_mirror);
+
+       return ret;
 }
 
 /*
@@ -404,50 +418,27 @@ static int csum_dirty_buffer(struct btrfs_root *root, struct page *page)
        struct extent_io_tree *tree;
        u64 start = (u64)page->index << PAGE_CACHE_SHIFT;
        u64 found_start;
-       unsigned long len;
        struct extent_buffer *eb;
-       int ret;
 
        tree = &BTRFS_I(page->mapping->host)->io_tree;
 
-       if (page->private == EXTENT_PAGE_PRIVATE) {
-               WARN_ON(1);
-               goto out;
-       }
-       if (!page->private) {
-               WARN_ON(1);
-               goto out;
-       }
-       len = page->private >> 2;
-       WARN_ON(len == 0);
-
-       eb = alloc_extent_buffer(tree, start, len, page);
-       if (eb == NULL) {
-               WARN_ON(1);
-               goto out;
-       }
-       ret = btree_read_extent_buffer_pages(root, eb, start + PAGE_CACHE_SIZE,
-                                            btrfs_header_generation(eb));
-       BUG_ON(ret);
-       WARN_ON(!btrfs_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN));
-
+       eb = (struct extent_buffer *)page->private;
+       if (page != eb->pages[0])
+               return 0;
        found_start = btrfs_header_bytenr(eb);
        if (found_start != start) {
                WARN_ON(1);
-               goto err;
+               return 0;
        }
-       if (eb->first_page != page) {
+       if (eb->pages[0] != page) {
                WARN_ON(1);
-               goto err;
+               return 0;
        }
        if (!PageUptodate(page)) {
                WARN_ON(1);
-               goto err;
+               return 0;
        }
        csum_tree_block(root, eb, 0);
-err:
-       free_extent_buffer(eb);
-out:
        return 0;
 }
 
@@ -537,34 +528,74 @@ static noinline int check_leaf(struct btrfs_root *root,
        return 0;
 }
 
+struct extent_buffer *find_eb_for_page(struct extent_io_tree *tree,
+                                      struct page *page, int max_walk)
+{
+       struct extent_buffer *eb;
+       u64 start = page_offset(page);
+       u64 target = start;
+       u64 min_start;
+
+       if (start < max_walk)
+               min_start = 0;
+       else
+               min_start = start - max_walk;
+
+       while (start >= min_start) {
+               eb = find_extent_buffer(tree, start, 0);
+               if (eb) {
+                       /*
+                        * we found an extent buffer and it contains our page
+                        * horray!
+                        */
+                       if (eb->start <= target &&
+                           eb->start + eb->len > target)
+                               return eb;
+
+                       /* we found an extent buffer that wasn't for us */
+                       free_extent_buffer(eb);
+                       return NULL;
+               }
+               if (start == 0)
+                       break;
+               start -= PAGE_CACHE_SIZE;
+       }
+       return NULL;
+}
+
 static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
                               struct extent_state *state)
 {
        struct extent_io_tree *tree;
        u64 found_start;
        int found_level;
-       unsigned long len;
        struct extent_buffer *eb;
        struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
        int ret = 0;
+       int reads_done;
 
-       tree = &BTRFS_I(page->mapping->host)->io_tree;
-       if (page->private == EXTENT_PAGE_PRIVATE)
-               goto out;
        if (!page->private)
                goto out;
 
-       len = page->private >> 2;
-       WARN_ON(len == 0);
+       tree = &BTRFS_I(page->mapping->host)->io_tree;
+       eb = (struct extent_buffer *)page->private;
 
-       eb = alloc_extent_buffer(tree, start, len, page);
-       if (eb == NULL) {
+       /* the pending IO might have been the only thing that kept this buffer
+        * in memory.  Make sure we have a ref for all this other checks
+        */
+       extent_buffer_get(eb);
+
+       reads_done = atomic_dec_and_test(&eb->io_pages);
+       if (!reads_done)
+               goto err;
+
+       if (test_bit(EXTENT_BUFFER_IOERR, &eb->bflags)) {
                ret = -EIO;
-               goto out;
+               goto err;
        }
 
        found_start = btrfs_header_bytenr(eb);
-       if (found_start != start) {
+       if (found_start != eb->start) {
                printk_ratelimited(KERN_INFO "btrfs bad tree block start "
                               "%llu %llu\n",
                               (unsigned long long)found_start,
@@ -572,13 +603,6 @@ static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
                ret = -EIO;
                goto err;
        }
-       if (eb->first_page != page) {
-               printk(KERN_INFO "btrfs bad first page %lu %lu\n",
-                      eb->first_page->index, page->index);
-               WARN_ON(1);
-               ret = -EIO;
-               goto err;
-       }
        if (check_tree_block_fsid(root, eb)) {
                printk_ratelimited(KERN_INFO "btrfs bad fsid on block %llu\n",
                               (unsigned long long)eb->start);
@@ -606,48 +630,31 @@ static int btree_readpage_end_io_hook(struct page *page, u64 start, u64 end,
                ret = -EIO;
        }
 
-       end = min_t(u64, eb->len, PAGE_CACHE_SIZE);
-       end = eb->start + end - 1;
+       if (!ret)
+               set_extent_buffer_uptodate(eb);
 err:
        if (test_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags)) {
                clear_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags);
                btree_readahead_hook(root, eb, eb->start, ret);
        }
 
+       if (ret)
+               clear_extent_buffer_uptodate(eb);
        free_extent_buffer(eb);
 out:
        return ret;
 }
 
-static int btree_io_failed_hook(struct bio *failed_bio,
-                        struct page *page, u64 start, u64 end,
-                        int mirror_num, struct extent_state *state)
+static int btree_io_failed_hook(struct page *page, int failed_mirror)
 {
-       struct extent_io_tree *tree;
-       unsigned long len;
        struct extent_buffer *eb;
        struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
 
-       tree = &BTRFS_I(page->mapping->host)->io_tree;
-       if (page->private == EXTENT_PAGE_PRIVATE)
-               goto out;
-       if (!page->private)
-               goto out;
-
-       len = page->private >> 2;
-       WARN_ON(len == 0);
-
-       eb = alloc_extent_buffer(tree, start, len, page);
-       if (eb == NULL)
-               goto out;
-
-       if (test_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags)) {
-               clear_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags);
+       eb = (struct extent_buffer *)page->private;
+       set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
+       eb->failed_mirror = failed_mirror;
+       if (test_and_clear_bit(EXTENT_BUFFER_READAHEAD, &eb->bflags))
                btree_readahead_hook(root, eb, eb->start, -EIO);
-       }
-       free_extent_buffer(eb);
-
-out:
        return -EIO;    /* we fixed nothing */
 }
 
@@ -719,11 +726,14 @@ unsigned long btrfs_async_submit_limit(struct btrfs_fs_info *info)
 static void run_one_async_start(struct btrfs_work *work)
 {
        struct async_submit_bio *async;
+       int ret;
 
        async = container_of(work, struct  async_submit_bio, work);
-       async->submit_bio_start(async->inode, async->rw, async->bio,
-                              async->mirror_num, async->bio_flags,
-                              async->bio_offset);
+       ret = async->submit_bio_start(async->inode, async->rw, async->bio,
+                                     async->mirror_num, async->bio_flags,
+                                     async->bio_offset);
+       if (ret)
+               async->error = ret;
 }
 
 static void run_one_async_done(struct btrfs_work *work)
@@ -744,6 +754,12 @@ static void run_one_async_done(struct btrfs_work *work)
            waitqueue_active(&fs_info->async_submit_wait))
                wake_up(&fs_info->async_submit_wait);
 
+       /* If an error occured we just want to clean up the bio and move on */
+       if (async->error) {
+               bio_endio(async->bio, async->error);
+               return;
+       }
+
        async->submit_bio_done(async->inode, async->rw, async->bio,
                               async->mirror_num, async->bio_flags,
                               async->bio_offset);
@@ -785,6 +801,8 @@ int btrfs_wq_submit_bio(struct btrfs_fs_info *fs_info, struct inode *inode,
        async->bio_flags = bio_flags;
        async->bio_offset = bio_offset;
 
+       async->error = 0;
+
        atomic_inc(&fs_info->nr_async_submits);
 
        if (rw & REQ_SYNC)
@@ -806,15 +824,18 @@ static int btree_csum_one_bio(struct bio *bio)
        struct bio_vec *bvec = bio->bi_io_vec;
        int bio_index = 0;
        struct btrfs_root *root;
+       int ret = 0;
 
        WARN_ON(bio->bi_vcnt <= 0);
        while (bio_index < bio->bi_vcnt) {
                root = BTRFS_I(bvec->bv_page->mapping->host)->root;
-               csum_dirty_buffer(root, bvec->bv_page);
+               ret = csum_dirty_buffer(root, bvec->bv_page);
+               if (ret)
+                       break;
                bio_index++;
                bvec++;
        }
-       return 0;
+       return ret;
 }
 
 static int __btree_submit_bio_start(struct inode *inode, int rw,
@@ -826,8 +847,7 @@ static int __btree_submit_bio_start(struct inode *inode, int rw,
         * when we're called for a write, we're already in the async
         * submission context.  Just jump into btrfs_map_bio
         */
-       btree_csum_one_bio(bio);
-       return 0;
+       return btree_csum_one_bio(bio);
 }
 
 static int __btree_submit_bio_done(struct inode *inode, int rw, struct bio *bio,
@@ -847,15 +867,16 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
 {
        int ret;
 
-       ret = btrfs_bio_wq_end_io(BTRFS_I(inode)->root->fs_info,
-                                         bio, 1);
-       BUG_ON(ret);
-
        if (!(rw & REQ_WRITE)) {
+
                /*
                 * called for a read, do the setup so that checksum validation
                 * can happen in the async kernel threads
                 */
+               ret = btrfs_bio_wq_end_io(BTRFS_I(inode)->root->fs_info,
+                                         bio, 1);
+               if (ret)
+                       return ret;
                return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio,
                                     mirror_num, 0);
        }
@@ -893,34 +914,6 @@ static int btree_migratepage(struct address_space *mapping,
 }
 #endif
 
-static int btree_writepage(struct page *page, struct writeback_control *wbc)
-{
-       struct extent_io_tree *tree;
-       struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
-       struct extent_buffer *eb;
-       int was_dirty;
-
-       tree = &BTRFS_I(page->mapping->host)->io_tree;
-       if (!(current->flags & PF_MEMALLOC)) {
-               return extent_write_full_page(tree, page,
-                                             btree_get_extent, wbc);
-       }
-
-       redirty_page_for_writepage(wbc, page);
-       eb = btrfs_find_tree_block(root, page_offset(page), PAGE_CACHE_SIZE);
-       WARN_ON(!eb);
-
-       was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags);
-       if (!was_dirty) {
-               spin_lock(&root->fs_info->delalloc_lock);
-               root->fs_info->dirty_metadata_bytes += PAGE_CACHE_SIZE;
-               spin_unlock(&root->fs_info->delalloc_lock);
-       }
-       free_extent_buffer(eb);
-
-       unlock_page(page);
-       return 0;
-}
 
 static int btree_writepages(struct address_space *mapping,
                            struct writeback_control *wbc)
@@ -940,7 +933,7 @@ static int btree_writepages(struct address_space *mapping,
                if (num_dirty < thresh)
                        return 0;
        }
-       return extent_writepages(tree, mapping, btree_get_extent, wbc);
+       return btree_write_cache_pages(mapping, wbc);
 }
 
 static int btree_readpage(struct file *file, struct page *page)
@@ -952,16 +945,8 @@ static int btree_readpage(struct file *file, struct page *page)
 
 static int btree_releasepage(struct page *page, gfp_t gfp_flags)
 {
-       struct extent_io_tree *tree;
-       struct extent_map_tree *map;
-       int ret;
-
        if (PageWriteback(page) || PageDirty(page))
                return 0;
-
-       tree = &BTRFS_I(page->mapping->host)->io_tree;
-       map = &BTRFS_I(page->mapping->host)->extent_tree;
-
        /*
         * We need to mask out eg. __GFP_HIGHMEM and __GFP_DMA32 as we're doing
         * slab allocation from alloc_extent_state down the callchain where
@@ -969,18 +954,7 @@ static int btree_releasepage(struct page *page, gfp_t gfp_flags)
         */
        gfp_flags &= ~GFP_SLAB_BUG_MASK;
 
-       ret = try_release_extent_state(map, tree, page, gfp_flags);
-       if (!ret)
-               return 0;
-
-       ret = try_release_extent_buffer(tree, page);
-       if (ret == 1) {
-               ClearPagePrivate(page);
-               set_page_private(page, 0);
-               page_cache_release(page);
-       }
-
-       return ret;
+       return try_release_extent_buffer(page, gfp_flags);
 }
 
 static void btree_invalidatepage(struct page *page, unsigned long offset)
@@ -998,15 +972,28 @@ static void btree_invalidatepage(struct page *page, unsigned long offset)
        }
 }
 
+static int btree_set_page_dirty(struct page *page)
+{
+       struct extent_buffer *eb;
+
+       BUG_ON(!PagePrivate(page));
+       eb = (struct extent_buffer *)page->private;
+       BUG_ON(!eb);
+       BUG_ON(!test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
+       BUG_ON(!atomic_read(&eb->refs));
+       btrfs_assert_tree_locked(eb);
+       return __set_page_dirty_nobuffers(page);
+}
+
 static const struct address_space_operations btree_aops = {
        .readpage       = btree_readpage,
-       .writepage      = btree_writepage,
        .writepages     = btree_writepages,
        .releasepage    = btree_releasepage,
        .invalidatepage = btree_invalidatepage,
 #ifdef CONFIG_MIGRATION
        .migratepage    = btree_migratepage,
 #endif
+       .set_page_dirty = btree_set_page_dirty,
 };
 
 int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
@@ -1049,7 +1036,7 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, u32 blocksize,
        if (test_bit(EXTENT_BUFFER_CORRUPT, &buf->bflags)) {
                free_extent_buffer(buf);
                return -EIO;
-       } else if (extent_buffer_uptodate(io_tree, buf, NULL)) {
+       } else if (extent_buffer_uptodate(buf)) {
                *eb = buf;
        } else {
                free_extent_buffer(buf);
@@ -1074,20 +1061,20 @@ struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
        struct extent_buffer *eb;
 
        eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
-                                bytenr, blocksize, NULL);
+                                bytenr, blocksize);
        return eb;
 }
 
 
 int btrfs_write_tree_block(struct extent_buffer *buf)
 {
-       return filemap_fdatawrite_range(buf->first_page->mapping, buf->start,
+       return filemap_fdatawrite_range(buf->pages[0]->mapping, buf->start,
                                        buf->start + buf->len - 1);
 }
 
 int btrfs_wait_tree_block_writeback(struct extent_buffer *buf)
 {
-       return filemap_fdatawait_range(buf->first_page->mapping,
+       return filemap_fdatawait_range(buf->pages[0]->mapping,
                                       buf->start, buf->start + buf->len - 1);
 }
 
@@ -1102,17 +1089,13 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
                return NULL;
 
        ret = btree_read_extent_buffer_pages(root, buf, 0, parent_transid);
-
-       if (ret == 0)
-               set_bit(EXTENT_BUFFER_UPTODATE, &buf->bflags);
        return buf;
 
 }
 
-int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-                    struct extent_buffer *buf)
+void clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+                     struct extent_buffer *buf)
 {
-       struct inode *btree_inode = root->fs_info->btree_inode;
        if (btrfs_header_generation(buf) ==
            root->fs_info->running_transaction->transid) {
                btrfs_assert_tree_locked(buf);
@@ -1121,23 +1104,27 @@ int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                        spin_lock(&root->fs_info->delalloc_lock);
                        if (root->fs_info->dirty_metadata_bytes >= buf->len)
                                root->fs_info->dirty_metadata_bytes -= buf->len;
-                       else
-                               WARN_ON(1);
+                       else {
+                               spin_unlock(&root->fs_info->delalloc_lock);
+                               btrfs_panic(root->fs_info, -EOVERFLOW,
+                                         "Can't clear %lu bytes from "
+                                         " dirty_mdatadata_bytes (%lu)",
+                                         buf->len,
+                                         root->fs_info->dirty_metadata_bytes);
+                       }
                        spin_unlock(&root->fs_info->delalloc_lock);
                }
 
                /* ugh, clear_extent_buffer_dirty needs to lock the page */
                btrfs_set_lock_blocking(buf);
-               clear_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree,
-                                         buf);
+               clear_extent_buffer_dirty(buf);
        }
-       return 0;
 }
 
-static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
-                       u32 stripesize, struct btrfs_root *root,
-                       struct btrfs_fs_info *fs_info,
-                       u64 objectid)
+static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
+                        u32 stripesize, struct btrfs_root *root,
+                        struct btrfs_fs_info *fs_info,
+                        u64 objectid)
 {
        root->node = NULL;
        root->commit_root = NULL;
@@ -1189,13 +1176,12 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
        root->defrag_running = 0;
        root->root_key.objectid = objectid;
        root->anon_dev = 0;
-       return 0;
 }
 
-static int find_and_setup_root(struct btrfs_root *tree_root,
-                              struct btrfs_fs_info *fs_info,
-                              u64 objectid,
-                              struct btrfs_root *root)
+static int __must_check find_and_setup_root(struct btrfs_root *tree_root,
+                                           struct btrfs_fs_info *fs_info,
+                                           u64 objectid,
+                                           struct btrfs_root *root)
 {
        int ret;
        u32 blocksize;
@@ -1208,7 +1194,8 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
                                   &root->root_item, &root->root_key);
        if (ret > 0)
                return -ENOENT;
-       BUG_ON(ret);
+       else if (ret < 0)
+               return ret;
 
        generation = btrfs_root_generation(&root->root_item);
        blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
@@ -1377,7 +1364,7 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
        root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
                                     blocksize, generation);
        root->commit_root = btrfs_root_node(root);
-       BUG_ON(!root->node);
+       BUG_ON(!root->node); /* -ENOMEM */
 out:
        if (location->objectid != BTRFS_TREE_LOG_OBJECTID) {
                root->ref_cows = 1;
@@ -1513,41 +1500,6 @@ static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi)
        return 0;
 }
 
-static int bio_ready_for_csum(struct bio *bio)
-{
-       u64 length = 0;
-       u64 buf_len = 0;
-       u64 start = 0;
-       struct page *page;
-       struct extent_io_tree *io_tree = NULL;
-       struct bio_vec *bvec;
-       int i;
-       int ret;
-
-       bio_for_each_segment(bvec, bio, i) {
-               page = bvec->bv_page;
-               if (page->private == EXTENT_PAGE_PRIVATE) {
-                       length += bvec->bv_len;
-                       continue;
-               }
-               if (!page->private) {
-                       length += bvec->bv_len;
-                       continue;
-               }
-               length = bvec->bv_len;
-               buf_len = page->private >> 2;
-               start = page_offset(page) + bvec->bv_offset;
-               io_tree = &BTRFS_I(page->mapping->host)->io_tree;
-       }
-       /* are we fully contained in this bio? */
-       if (buf_len <= length)
-               return 1;
-
-       ret = extent_range_uptodate(io_tree, start + length,
-                                   start + buf_len - 1);
-       return ret;
-}
-
 /*
  * called by the kthread helper functions to finally call the bio end_io
  * functions.  This is where read checksum verification actually happens
@@ -1563,17 +1515,6 @@ static void end_workqueue_fn(struct btrfs_work *work)
        bio = end_io_wq->bio;
        fs_info = end_io_wq->info;
 
-       /* metadata bio reads are special because the whole tree block must
-        * be checksummed at once.  This makes sure the entire block is in
-        * ram and up to date before trying to verify things.  For
-        * blocksize <= pagesize, it is basically a noop
-        */
-       if (!(bio->bi_rw & REQ_WRITE) && end_io_wq->metadata &&
-           !bio_ready_for_csum(bio)) {
-               btrfs_queue_worker(&fs_info->endio_meta_workers,
-                                  &end_io_wq->work);
-               return;
-       }
        error = end_io_wq->error;
        bio->bi_private = end_io_wq->private;
        bio->bi_end_io = end_io_wq->end_io;
@@ -1614,9 +1555,10 @@ static int transaction_kthread(void *arg)
        u64 transid;
        unsigned long now;
        unsigned long delay;
-       int ret;
+       bool cannot_commit;
 
        do {
+               cannot_commit = false;
                delay = HZ * 30;
                vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
                mutex_lock(&root->fs_info->transaction_kthread_mutex);
@@ -1638,11 +1580,14 @@ static int transaction_kthread(void *arg)
                transid = cur->transid;
                spin_unlock(&root->fs_info->trans_lock);
 
+               /* If the file system is aborted, this will always fail. */
                trans = btrfs_join_transaction(root);
-               BUG_ON(IS_ERR(trans));
+               if (IS_ERR(trans)) {
+                       cannot_commit = true;
+                       goto sleep;
+               }
                if (transid == trans->transid) {
-                       ret = btrfs_commit_transaction(trans, root);
-                       BUG_ON(ret);
+                       btrfs_commit_transaction(trans, root);
                } else {
                        btrfs_end_transaction(trans, root);
                }
@@ -1653,7 +1598,8 @@ sleep:
                if (!try_to_freeze()) {
                        set_current_state(TASK_INTERRUPTIBLE);
                        if (!kthread_should_stop() &&
-                           !btrfs_transaction_blocked(root->fs_info))
+                           (!btrfs_transaction_blocked(root->fs_info) ||
+                            cannot_commit))
                                schedule_timeout(delay);
                        __set_current_state(TASK_RUNNING);
                }
@@ -2042,6 +1988,7 @@ int open_ctree(struct super_block *sb,
        RB_CLEAR_NODE(&BTRFS_I(fs_info->btree_inode)->rb_node);
        extent_io_tree_init(&BTRFS_I(fs_info->btree_inode)->io_tree,
                             fs_info->btree_inode->i_mapping);
+       BTRFS_I(fs_info->btree_inode)->io_tree.track_uptodate = 0;
        extent_map_tree_init(&BTRFS_I(fs_info->btree_inode)->extent_tree);
 
        BTRFS_I(fs_info->btree_inode)->io_tree.ops = &btree_extent_io_ops;
@@ -2084,6 +2031,7 @@ int open_ctree(struct super_block *sb,
        __setup_root(4096, 4096, 4096, 4096, tree_root,
                     fs_info, BTRFS_ROOT_TREE_OBJECTID);
 
+       invalidate_bdev(fs_devices->latest_bdev);
        bh = btrfs_read_dev_super(fs_devices->latest_bdev);
        if (!bh) {
                err = -EINVAL;
@@ -2104,7 +2052,12 @@ int open_ctree(struct super_block *sb,
        /* check FS state, whether FS is broken. */
        fs_info->fs_state |= btrfs_super_flags(disk_super);
 
-       btrfs_check_super_valid(fs_info, sb->s_flags & MS_RDONLY);
+       ret = btrfs_check_super_valid(fs_info, sb->s_flags & MS_RDONLY);
+       if (ret) {
+               printk(KERN_ERR "btrfs: superblock contains fatal errors\n");
+               err = ret;
+               goto fail_alloc;
+       }
 
        /*
         * run through our array of backup supers and setup
@@ -2135,10 +2088,55 @@ int open_ctree(struct super_block *sb,
                goto fail_alloc;
        }
 
+       if (btrfs_super_leafsize(disk_super) !=
+           btrfs_super_nodesize(disk_super)) {
+               printk(KERN_ERR "BTRFS: couldn't mount because metadata "
+                      "blocksizes don't match.  node %d leaf %d\n",
+                      btrfs_super_nodesize(disk_super),
+                      btrfs_super_leafsize(disk_super));
+               err = -EINVAL;
+               goto fail_alloc;
+       }
+       if (btrfs_super_leafsize(disk_super) > BTRFS_MAX_METADATA_BLOCKSIZE) {
+               printk(KERN_ERR "BTRFS: couldn't mount because metadata "
+                      "blocksize (%d) was too large\n",
+                      btrfs_super_leafsize(disk_super));
+               err = -EINVAL;
+               goto fail_alloc;
+       }
+
        features = btrfs_super_incompat_flags(disk_super);
        features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
        if (tree_root->fs_info->compress_type & BTRFS_COMPRESS_LZO)
                features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO;
+
+       /*
+        * flag our filesystem as having big metadata blocks if
+        * they are bigger than the page size
+        */
+       if (btrfs_super_leafsize(disk_super) > PAGE_CACHE_SIZE) {
+               if (!(features & BTRFS_FEATURE_INCOMPAT_BIG_METADATA))
+                       printk(KERN_INFO "btrfs flagging fs with big metadata feature\n");
+               features |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
+       }
+
+       nodesize = btrfs_super_nodesize(disk_super);
+       leafsize = btrfs_super_leafsize(disk_super);
+       sectorsize = btrfs_super_sectorsize(disk_super);
+       stripesize = btrfs_super_stripesize(disk_super);
+
+       /*
+        * mixed block groups end up with duplicate but slightly offset
+        * extent buffers for the same range.  It leads to corruptions
+        */
+       if ((features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS) &&
+           (sectorsize != leafsize)) {
+               printk(KERN_WARNING "btrfs: unequal leaf/node/sector sizes "
+                               "are not allowed for mixed block groups on %s\n",
+                               sb->s_id);
+               goto fail_alloc;
+       }
+
        btrfs_set_super_incompat_flags(disk_super, features);
 
        features = btrfs_super_compat_ro_flags(disk_super) &
@@ -2242,10 +2240,6 @@ int open_ctree(struct super_block *sb,
        fs_info->bdi.ra_pages = max(fs_info->bdi.ra_pages,
                                    4 * 1024 * 1024 / PAGE_CACHE_SIZE);
 
-       nodesize = btrfs_super_nodesize(disk_super);
-       leafsize = btrfs_super_leafsize(disk_super);
-       sectorsize = btrfs_super_sectorsize(disk_super);
-       stripesize = btrfs_super_stripesize(disk_super);
        tree_root->nodesize = nodesize;
        tree_root->leafsize = leafsize;
        tree_root->sectorsize = sectorsize;
@@ -2285,7 +2279,7 @@ int open_ctree(struct super_block *sb,
        chunk_root->node = read_tree_block(chunk_root,
                                           btrfs_super_chunk_root(disk_super),
                                           blocksize, generation);
-       BUG_ON(!chunk_root->node);
+       BUG_ON(!chunk_root->node); /* -ENOMEM */
        if (!test_bit(EXTENT_BUFFER_UPTODATE, &chunk_root->node->bflags)) {
                printk(KERN_WARNING "btrfs: failed to read chunk root on %s\n",
                       sb->s_id);
@@ -2425,21 +2419,31 @@ retry_root_backup:
                log_tree_root->node = read_tree_block(tree_root, bytenr,
                                                      blocksize,
                                                      generation + 1);
+               /* returns with log_tree_root freed on success */
                ret = btrfs_recover_log_trees(log_tree_root);
-               BUG_ON(ret);
+               if (ret) {
+                       btrfs_error(tree_root->fs_info, ret,
+                                   "Failed to recover log tree");
+                       free_extent_buffer(log_tree_root->node);
+                       kfree(log_tree_root);
+                       goto fail_trans_kthread;
+               }
 
                if (sb->s_flags & MS_RDONLY) {
-                       ret =  btrfs_commit_super(tree_root);
-                       BUG_ON(ret);
+                       ret = btrfs_commit_super(tree_root);
+                       if (ret)
+                               goto fail_trans_kthread;
                }
        }
 
        ret = btrfs_find_orphan_roots(tree_root);
-       BUG_ON(ret);
+       if (ret)
+               goto fail_trans_kthread;
 
        if (!(sb->s_flags & MS_RDONLY)) {
                ret = btrfs_cleanup_fs_roots(fs_info);
-               BUG_ON(ret);
+               if (ret) {
+                       }
 
                ret = btrfs_recover_relocation(tree_root);
                if (ret < 0) {
@@ -2859,6 +2863,8 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
        if (total_errors > max_errors) {
                printk(KERN_ERR "btrfs: %d errors while writing supers\n",
                       total_errors);
+
+               /* This shouldn't happen. FUA is masked off if unsupported */
                BUG();
        }
 
@@ -2875,9 +2881,9 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
        }
        mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
        if (total_errors > max_errors) {
-               printk(KERN_ERR "btrfs: %d errors while writing supers\n",
-                      total_errors);
-               BUG();
+               btrfs_error(root->fs_info, -EIO,
+                           "%d errors while writing supers", total_errors);
+               return -EIO;
        }
        return 0;
 }
@@ -2891,7 +2897,20 @@ int write_ctree_super(struct btrfs_trans_handle *trans,
        return ret;
 }
 
-int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
+/* Kill all outstanding I/O */
+void btrfs_abort_devices(struct btrfs_root *root)
+{
+       struct list_head *head;
+       struct btrfs_device *dev;
+       mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
+       head = &root->fs_info->fs_devices->devices;
+       list_for_each_entry_rcu(dev, head, dev_list) {
+               blk_abort_queue(dev->bdev->bd_disk->queue);
+       }
+       mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
+}
+
+void btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
 {
        spin_lock(&fs_info->fs_roots_radix_lock);
        radix_tree_delete(&fs_info->fs_roots_radix,
@@ -2904,7 +2923,6 @@ int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
        __btrfs_remove_free_space_cache(root->free_ino_pinned);
        __btrfs_remove_free_space_cache(root->free_ino_ctl);
        free_fs_root(root);
-       return 0;
 }
 
 static void free_fs_root(struct btrfs_root *root)
@@ -2921,7 +2939,7 @@ static void free_fs_root(struct btrfs_root *root)
        kfree(root);
 }
 
-static int del_fs_roots(struct btrfs_fs_info *fs_info)
+static void del_fs_roots(struct btrfs_fs_info *fs_info)
 {
        int ret;
        struct btrfs_root *gang[8];
@@ -2950,7 +2968,6 @@ static int del_fs_roots(struct btrfs_fs_info *fs_info)
                for (i = 0; i < ret; i++)
                        btrfs_free_fs_root(fs_info, gang[i]);
        }
-       return 0;
 }
 
 int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info)
@@ -2999,14 +3016,21 @@ int btrfs_commit_super(struct btrfs_root *root)
        if (IS_ERR(trans))
                return PTR_ERR(trans);
        ret = btrfs_commit_transaction(trans, root);
-       BUG_ON(ret);
+       if (ret)
+               return ret;
        /* run commit again to drop the original snapshot */
        trans = btrfs_join_transaction(root);
        if (IS_ERR(trans))
                return PTR_ERR(trans);
-       btrfs_commit_transaction(trans, root);
+       ret = btrfs_commit_transaction(trans, root);
+       if (ret)
+               return ret;
        ret = btrfs_write_and_wait_transaction(NULL, root);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_error(root->fs_info, ret,
+                           "Failed to sync btree inode to disk.");
+               return ret;
+       }
 
        ret = write_ctree_super(NULL, root, 0);
        return ret;
@@ -3122,10 +3146,9 @@ int close_ctree(struct btrfs_root *root)
 int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
 {
        int ret;
-       struct inode *btree_inode = buf->first_page->mapping->host;
+       struct inode *btree_inode = buf->pages[0]->mapping->host;
 
-       ret = extent_buffer_uptodate(&BTRFS_I(btree_inode)->io_tree, buf,
-                                    NULL);
+       ret = extent_buffer_uptodate(buf);
        if (!ret)
                return ret;
 
@@ -3136,16 +3159,13 @@ int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
 
 int btrfs_set_buffer_uptodate(struct extent_buffer *buf)
 {
-       struct inode *btree_inode = buf->first_page->mapping->host;
-       return set_extent_buffer_uptodate(&BTRFS_I(btree_inode)->io_tree,
-                                         buf);
+       return set_extent_buffer_uptodate(buf);
 }
 
 void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
 {
-       struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root;
+       struct btrfs_root *root = BTRFS_I(buf->pages[0]->mapping->host)->root;
        u64 transid = btrfs_header_generation(buf);
-       struct inode *btree_inode = root->fs_info->btree_inode;
        int was_dirty;
 
        btrfs_assert_tree_locked(buf);
@@ -3157,8 +3177,7 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
                        (unsigned long long)root->fs_info->generation);
                WARN_ON(1);
        }
-       was_dirty = set_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree,
-                                           buf);
+       was_dirty = set_extent_buffer_dirty(buf);
        if (!was_dirty) {
                spin_lock(&root->fs_info->delalloc_lock);
                root->fs_info->dirty_metadata_bytes += buf->len;
@@ -3212,12 +3231,8 @@ void __btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr)
 
 int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid)
 {
-       struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root;
-       int ret;
-       ret = btree_read_extent_buffer_pages(root, buf, 0, parent_transid);
-       if (ret == 0)
-               set_bit(EXTENT_BUFFER_UPTODATE, &buf->bflags);
-       return ret;
+       struct btrfs_root *root = BTRFS_I(buf->pages[0]->mapping->host)->root;
+       return btree_read_extent_buffer_pages(root, buf, 0, parent_transid);
 }
 
 static int btree_lock_page_hook(struct page *page, void *data,
@@ -3225,17 +3240,21 @@ static int btree_lock_page_hook(struct page *page, void *data,
 {
        struct inode *inode = page->mapping->host;
        struct btrfs_root *root = BTRFS_I(inode)->root;
-       struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        struct extent_buffer *eb;
-       unsigned long len;
-       u64 bytenr = page_offset(page);
 
-       if (page->private == EXTENT_PAGE_PRIVATE)
+       /*
+        * We culled this eb but the page is still hanging out on the mapping,
+        * carry on.
+        */
+       if (!PagePrivate(page))
                goto out;
 
-       len = page->private >> 2;
-       eb = find_extent_buffer(io_tree, bytenr, len);
-       if (!eb)
+       eb = (struct extent_buffer *)page->private;
+       if (!eb) {
+               WARN_ON(1);
+               goto out;
+       }
+       if (page != eb->pages[0])
                goto out;
 
        if (!btrfs_try_tree_write_lock(eb)) {
@@ -3254,7 +3273,6 @@ static int btree_lock_page_hook(struct page *page, void *data,
        }
 
        btrfs_tree_unlock(eb);
-       free_extent_buffer(eb);
 out:
        if (!trylock_page(page)) {
                flush_fn(data);
@@ -3263,15 +3281,23 @@ out:
        return 0;
 }
 
-static void btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
+static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
                              int read_only)
 {
+       if (btrfs_super_csum_type(fs_info->super_copy) >= ARRAY_SIZE(btrfs_csum_sizes)) {
+               printk(KERN_ERR "btrfs: unsupported checksum algorithm\n");
+               return -EINVAL;
+       }
+
        if (read_only)
-               return;
+               return 0;
 
-       if (fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR)
+       if (fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
                printk(KERN_WARNING "warning: mount fs with errors, "
                       "running btrfsck is recommended\n");
+       }
+
+       return 0;
 }
 
 int btrfs_error_commit_super(struct btrfs_root *root)
@@ -3293,7 +3319,7 @@ int btrfs_error_commit_super(struct btrfs_root *root)
        return ret;
 }
 
-static int btrfs_destroy_ordered_operations(struct btrfs_root *root)
+static void btrfs_destroy_ordered_operations(struct btrfs_root *root)
 {
        struct btrfs_inode *btrfs_inode;
        struct list_head splice;
@@ -3315,11 +3341,9 @@ static int btrfs_destroy_ordered_operations(struct btrfs_root *root)
 
        spin_unlock(&root->fs_info->ordered_extent_lock);
        mutex_unlock(&root->fs_info->ordered_operations_mutex);
-
-       return 0;
 }
 
-static int btrfs_destroy_ordered_extents(struct btrfs_root *root)
+static void btrfs_destroy_ordered_extents(struct btrfs_root *root)
 {
        struct list_head splice;
        struct btrfs_ordered_extent *ordered;
@@ -3351,12 +3375,10 @@ static int btrfs_destroy_ordered_extents(struct btrfs_root *root)
        }
 
        spin_unlock(&root->fs_info->ordered_extent_lock);
-
-       return 0;
 }
 
-static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
-                                     struct btrfs_root *root)
+int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
+                              struct btrfs_root *root)
 {
        struct rb_node *node;
        struct btrfs_delayed_ref_root *delayed_refs;
@@ -3365,6 +3387,7 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
 
        delayed_refs = &trans->delayed_refs;
 
+again:
        spin_lock(&delayed_refs->lock);
        if (delayed_refs->num_entries == 0) {
                spin_unlock(&delayed_refs->lock);
@@ -3386,6 +3409,7 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
                        struct btrfs_delayed_ref_head *head;
 
                        head = btrfs_delayed_node_to_head(ref);
+                       spin_unlock(&delayed_refs->lock);
                        mutex_lock(&head->mutex);
                        kfree(head->extent_op);
                        delayed_refs->num_heads--;
@@ -3393,8 +3417,9 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
                                delayed_refs->num_heads_ready--;
                        list_del_init(&head->cluster);
                        mutex_unlock(&head->mutex);
+                       btrfs_put_delayed_ref(ref);
+                       goto again;
                }
-
                spin_unlock(&delayed_refs->lock);
                btrfs_put_delayed_ref(ref);
 
@@ -3407,7 +3432,7 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
        return ret;
 }
 
-static int btrfs_destroy_pending_snapshots(struct btrfs_transaction *t)
+static void btrfs_destroy_pending_snapshots(struct btrfs_transaction *t)
 {
        struct btrfs_pending_snapshot *snapshot;
        struct list_head splice;
@@ -3425,11 +3450,9 @@ static int btrfs_destroy_pending_snapshots(struct btrfs_transaction *t)
 
                kfree(snapshot);
        }
-
-       return 0;
 }
 
-static int btrfs_destroy_delalloc_inodes(struct btrfs_root *root)
+static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root)
 {
        struct btrfs_inode *btrfs_inode;
        struct list_head splice;
@@ -3449,8 +3472,6 @@ static int btrfs_destroy_delalloc_inodes(struct btrfs_root *root)
        }
 
        spin_unlock(&root->fs_info->delalloc_lock);
-
-       return 0;
 }
 
 static int btrfs_destroy_marked_extents(struct btrfs_root *root,
@@ -3541,13 +3562,43 @@ static int btrfs_destroy_pinned_extent(struct btrfs_root *root,
        return 0;
 }
 
-static int btrfs_cleanup_transaction(struct btrfs_root *root)
+void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
+                                  struct btrfs_root *root)
+{
+       btrfs_destroy_delayed_refs(cur_trans, root);
+       btrfs_block_rsv_release(root, &root->fs_info->trans_block_rsv,
+                               cur_trans->dirty_pages.dirty_bytes);
+
+       /* FIXME: cleanup wait for commit */
+       cur_trans->in_commit = 1;
+       cur_trans->blocked = 1;
+       if (waitqueue_active(&root->fs_info->transaction_blocked_wait))
+               wake_up(&root->fs_info->transaction_blocked_wait);
+
+       cur_trans->blocked = 0;
+       if (waitqueue_active(&root->fs_info->transaction_wait))
+               wake_up(&root->fs_info->transaction_wait);
+
+       cur_trans->commit_done = 1;
+       if (waitqueue_active(&cur_trans->commit_wait))
+               wake_up(&cur_trans->commit_wait);
+
+       btrfs_destroy_pending_snapshots(cur_trans);
+
+       btrfs_destroy_marked_extents(root, &cur_trans->dirty_pages,
+                                    EXTENT_DIRTY);
+
+       /*
+       memset(cur_trans, 0, sizeof(*cur_trans));
+       kmem_cache_free(btrfs_transaction_cachep, cur_trans);
+       */
+}
+
+int btrfs_cleanup_transaction(struct btrfs_root *root)
 {
        struct btrfs_transaction *t;
        LIST_HEAD(list);
 
-       WARN_ON(1);
-
        mutex_lock(&root->fs_info->transaction_kthread_mutex);
 
        spin_lock(&root->fs_info->trans_lock);
@@ -3612,6 +3663,17 @@ static int btrfs_cleanup_transaction(struct btrfs_root *root)
        return 0;
 }
 
+static int btree_writepage_io_failed_hook(struct bio *bio, struct page *page,
+                                         u64 start, u64 end,
+                                         struct extent_state *state)
+{
+       struct super_block *sb = page->mapping->host->i_sb;
+       struct btrfs_fs_info *fs_info = btrfs_sb(sb);
+       btrfs_error(fs_info, -EIO,
+                   "Error occured while writing out btree at %llu", start);
+       return -EIO;
+}
+
 static struct extent_io_ops btree_extent_io_ops = {
        .write_cache_pages_lock_hook = btree_lock_page_hook,
        .readpage_end_io_hook = btree_readpage_end_io_hook,
@@ -3619,4 +3681,5 @@ static struct extent_io_ops btree_extent_io_ops = {
        .submit_bio_hook = btree_submit_bio_hook,
        /* note we're sharing with inode.c for the merge bio hook */
        .merge_bio_hook = btrfs_merge_bio_hook,
+       .writepage_io_failed_hook = btree_writepage_io_failed_hook,
 };
index e4bc4741319bd3a1e094de03566feeb360d35e4d..a7ace1a2dd12516a57102e44591f881e922ed89d 100644 (file)
@@ -44,8 +44,8 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, u32 blocksize,
                         int mirror_num, struct extent_buffer **eb);
 struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
                                                   u64 bytenr, u32 blocksize);
-int clean_tree_block(struct btrfs_trans_handle *trans,
-                    struct btrfs_root *root, struct extent_buffer *buf);
+void clean_tree_block(struct btrfs_trans_handle *trans,
+                     struct btrfs_root *root, struct extent_buffer *buf);
 int open_ctree(struct super_block *sb,
               struct btrfs_fs_devices *fs_devices,
               char *options);
@@ -64,7 +64,7 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
 int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info);
 void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr);
 void __btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr);
-int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root);
+void btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root);
 void btrfs_mark_buffer_dirty(struct extent_buffer *buf);
 int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid);
 int btrfs_set_buffer_uptodate(struct extent_buffer *buf);
@@ -85,6 +85,10 @@ int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
                             struct btrfs_fs_info *fs_info);
 int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
                       struct btrfs_root *root);
+int btrfs_cleanup_transaction(struct btrfs_root *root);
+void btrfs_cleanup_one_transaction(struct btrfs_transaction *trans,
+                                 struct btrfs_root *root);
+void btrfs_abort_devices(struct btrfs_root *root);
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 void btrfs_init_lockdep(void);
index 5f77166fd01c7eb3d33cb78666e31822fc0a1083..e887ee62b6d4ba0a98f7e2437323eecfca88bf23 100644 (file)
@@ -193,7 +193,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child)
        if (ret < 0)
                goto fail;
 
-       BUG_ON(ret == 0);
+       BUG_ON(ret == 0); /* Key with offset of -1 found */
        if (path->slots[0] == 0) {
                ret = -ENOENT;
                goto fail;
index 37e0a800d34e3a867437a9c1b2072e51a7ccc743..a84420491c11075093cc2d76f4c080953f9fc55b 100644 (file)
@@ -245,7 +245,7 @@ static int exclude_super_stripes(struct btrfs_root *root,
                cache->bytes_super += stripe_len;
                ret = add_excluded_extent(root, cache->key.objectid,
                                          stripe_len);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
 
        for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
@@ -253,13 +253,13 @@ static int exclude_super_stripes(struct btrfs_root *root,
                ret = btrfs_rmap_block(&root->fs_info->mapping_tree,
                                       cache->key.objectid, bytenr,
                                       0, &logical, &nr, &stripe_len);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
 
                while (nr--) {
                        cache->bytes_super += stripe_len;
                        ret = add_excluded_extent(root, logical[nr],
                                                  stripe_len);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
                }
 
                kfree(logical);
@@ -321,7 +321,7 @@ static u64 add_new_free_space(struct btrfs_block_group_cache *block_group,
                        total_added += size;
                        ret = btrfs_add_free_space(block_group, start,
                                                   size);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM or logic error */
                        start = extent_end + 1;
                } else {
                        break;
@@ -332,7 +332,7 @@ static u64 add_new_free_space(struct btrfs_block_group_cache *block_group,
                size = end - start;
                total_added += size;
                ret = btrfs_add_free_space(block_group, start, size);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM or logic error */
        }
 
        return total_added;
@@ -474,7 +474,8 @@ static int cache_block_group(struct btrfs_block_group_cache *cache,
        int ret = 0;
 
        caching_ctl = kzalloc(sizeof(*caching_ctl), GFP_NOFS);
-       BUG_ON(!caching_ctl);
+       if (!caching_ctl)
+               return -ENOMEM;
 
        INIT_LIST_HEAD(&caching_ctl->list);
        mutex_init(&caching_ctl->mutex);
@@ -982,7 +983,7 @@ static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
                                ret = btrfs_next_leaf(root, path);
                                if (ret < 0)
                                        return ret;
-                               BUG_ON(ret > 0);
+                               BUG_ON(ret > 0); /* Corruption */
                                leaf = path->nodes[0];
                        }
                        btrfs_item_key_to_cpu(leaf, &found_key,
@@ -1008,9 +1009,9 @@ static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
                                new_size + extra_size, 1);
        if (ret < 0)
                return ret;
-       BUG_ON(ret);
+       BUG_ON(ret); /* Corruption */
 
-       ret = btrfs_extend_item(trans, root, path, new_size);
+       btrfs_extend_item(trans, root, path, new_size);
 
        leaf = path->nodes[0];
        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
@@ -1478,7 +1479,11 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
                err = ret;
                goto out;
        }
-       BUG_ON(ret);
+       if (ret && !insert) {
+               err = -ENOENT;
+               goto out;
+       }
+       BUG_ON(ret); /* Corruption */
 
        leaf = path->nodes[0];
        item_size = btrfs_item_size_nr(leaf, path->slots[0]);
@@ -1592,13 +1597,13 @@ out:
  * helper to add new inline back ref
  */
 static noinline_for_stack
-int setup_inline_extent_backref(struct btrfs_trans_handle *trans,
-                               struct btrfs_root *root,
-                               struct btrfs_path *path,
-                               struct btrfs_extent_inline_ref *iref,
-                               u64 parent, u64 root_objectid,
-                               u64 owner, u64 offset, int refs_to_add,
-                               struct btrfs_delayed_extent_op *extent_op)
+void setup_inline_extent_backref(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct btrfs_path *path,
+                                struct btrfs_extent_inline_ref *iref,
+                                u64 parent, u64 root_objectid,
+                                u64 owner, u64 offset, int refs_to_add,
+                                struct btrfs_delayed_extent_op *extent_op)
 {
        struct extent_buffer *leaf;
        struct btrfs_extent_item *ei;
@@ -1608,7 +1613,6 @@ int setup_inline_extent_backref(struct btrfs_trans_handle *trans,
        u64 refs;
        int size;
        int type;
-       int ret;
 
        leaf = path->nodes[0];
        ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
@@ -1617,7 +1621,7 @@ int setup_inline_extent_backref(struct btrfs_trans_handle *trans,
        type = extent_ref_type(parent, owner);
        size = btrfs_extent_inline_ref_size(type);
 
-       ret = btrfs_extend_item(trans, root, path, size);
+       btrfs_extend_item(trans, root, path, size);
 
        ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
        refs = btrfs_extent_refs(leaf, ei);
@@ -1652,7 +1656,6 @@ int setup_inline_extent_backref(struct btrfs_trans_handle *trans,
                btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
        }
        btrfs_mark_buffer_dirty(leaf);
-       return 0;
 }
 
 static int lookup_extent_backref(struct btrfs_trans_handle *trans,
@@ -1687,12 +1690,12 @@ static int lookup_extent_backref(struct btrfs_trans_handle *trans,
  * helper to update/remove inline back ref
  */
 static noinline_for_stack
-int update_inline_extent_backref(struct btrfs_trans_handle *trans,
-                                struct btrfs_root *root,
-                                struct btrfs_path *path,
-                                struct btrfs_extent_inline_ref *iref,
-                                int refs_to_mod,
-                                struct btrfs_delayed_extent_op *extent_op)
+void update_inline_extent_backref(struct btrfs_trans_handle *trans,
+                                 struct btrfs_root *root,
+                                 struct btrfs_path *path,
+                                 struct btrfs_extent_inline_ref *iref,
+                                 int refs_to_mod,
+                                 struct btrfs_delayed_extent_op *extent_op)
 {
        struct extent_buffer *leaf;
        struct btrfs_extent_item *ei;
@@ -1703,7 +1706,6 @@ int update_inline_extent_backref(struct btrfs_trans_handle *trans,
        u32 item_size;
        int size;
        int type;
-       int ret;
        u64 refs;
 
        leaf = path->nodes[0];
@@ -1745,10 +1747,9 @@ int update_inline_extent_backref(struct btrfs_trans_handle *trans,
                        memmove_extent_buffer(leaf, ptr, ptr + size,
                                              end - ptr - size);
                item_size -= size;
-               ret = btrfs_truncate_item(trans, root, path, item_size, 1);
+               btrfs_truncate_item(trans, root, path, item_size, 1);
        }
        btrfs_mark_buffer_dirty(leaf);
-       return 0;
 }
 
 static noinline_for_stack
@@ -1768,13 +1769,13 @@ int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
                                           root_objectid, owner, offset, 1);
        if (ret == 0) {
                BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID);
-               ret = update_inline_extent_backref(trans, root, path, iref,
-                                                  refs_to_add, extent_op);
+               update_inline_extent_backref(trans, root, path, iref,
+                                            refs_to_add, extent_op);
        } else if (ret == -ENOENT) {
-               ret = setup_inline_extent_backref(trans, root, path, iref,
-                                                 parent, root_objectid,
-                                                 owner, offset, refs_to_add,
-                                                 extent_op);
+               setup_inline_extent_backref(trans, root, path, iref, parent,
+                                           root_objectid, owner, offset,
+                                           refs_to_add, extent_op);
+               ret = 0;
        }
        return ret;
 }
@@ -1804,12 +1805,12 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans,
                                 struct btrfs_extent_inline_ref *iref,
                                 int refs_to_drop, int is_data)
 {
-       int ret;
+       int ret = 0;
 
        BUG_ON(!is_data && refs_to_drop != 1);
        if (iref) {
-               ret = update_inline_extent_backref(trans, root, path, iref,
-                                                  -refs_to_drop, NULL);
+               update_inline_extent_backref(trans, root, path, iref,
+                                            -refs_to_drop, NULL);
        } else if (is_data) {
                ret = remove_extent_data_ref(trans, root, path, refs_to_drop);
        } else {
@@ -1835,6 +1836,7 @@ static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
        /* Tell the block device(s) that the sectors can be discarded */
        ret = btrfs_map_block(&root->fs_info->mapping_tree, REQ_DISCARD,
                              bytenr, &num_bytes, &bbio, 0);
+       /* Error condition is -ENOMEM */
        if (!ret) {
                struct btrfs_bio_stripe *stripe = bbio->stripes;
                int i;
@@ -1850,7 +1852,7 @@ static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
                        if (!ret)
                                discarded_bytes += stripe->length;
                        else if (ret != -EOPNOTSUPP)
-                               break;
+                               break; /* Logic errors or -ENOMEM, or -EIO but I don't know how that could happen JDM */
 
                        /*
                         * Just in case we get back EOPNOTSUPP for some reason,
@@ -1869,6 +1871,7 @@ static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr,
        return ret;
 }
 
+/* Can return -ENOMEM */
 int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
                         struct btrfs_root *root,
                         u64 bytenr, u64 num_bytes, u64 parent,
@@ -1944,7 +1947,8 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
        ret = insert_extent_backref(trans, root->fs_info->extent_root,
                                    path, bytenr, parent, root_objectid,
                                    owner, offset, refs_to_add);
-       BUG_ON(ret);
+       if (ret)
+               btrfs_abort_transaction(trans, root, ret);
 out:
        btrfs_free_path(path);
        return err;
@@ -2031,6 +2035,9 @@ static int run_delayed_extent_op(struct btrfs_trans_handle *trans,
        int ret;
        int err = 0;
 
+       if (trans->aborted)
+               return 0;
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -2128,7 +2135,11 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
                               struct btrfs_delayed_extent_op *extent_op,
                               int insert_reserved)
 {
-       int ret;
+       int ret = 0;
+
+       if (trans->aborted)
+               return 0;
+
        if (btrfs_delayed_ref_is_head(node)) {
                struct btrfs_delayed_ref_head *head;
                /*
@@ -2146,11 +2157,10 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
                                ret = btrfs_del_csums(trans, root,
                                                      node->bytenr,
                                                      node->num_bytes);
-                               BUG_ON(ret);
                        }
                }
                mutex_unlock(&head->mutex);
-               return 0;
+               return ret;
        }
 
        if (node->type == BTRFS_TREE_BLOCK_REF_KEY ||
@@ -2197,6 +2207,10 @@ again:
        return NULL;
 }
 
+/*
+ * Returns 0 on success or if called with an already aborted transaction.
+ * Returns -ENOMEM or -EIO on failure and will abort the transaction.
+ */
 static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
                                       struct btrfs_root *root,
                                       struct list_head *cluster)
@@ -2285,9 +2299,13 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
 
                                ret = run_delayed_extent_op(trans, root,
                                                            ref, extent_op);
-                               BUG_ON(ret);
                                kfree(extent_op);
 
+                               if (ret) {
+                                       printk(KERN_DEBUG "btrfs: run_delayed_extent_op returned %d\n", ret);
+                                       return ret;
+                               }
+
                                goto next;
                        }
 
@@ -2308,11 +2326,16 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
 
                ret = run_one_delayed_ref(trans, root, ref, extent_op,
                                          must_insert_reserved);
-               BUG_ON(ret);
 
                btrfs_put_delayed_ref(ref);
                kfree(extent_op);
                count++;
+
+               if (ret) {
+                       printk(KERN_DEBUG "btrfs: run_one_delayed_ref returned %d\n", ret);
+                       return ret;
+               }
+
 next:
                do_chunk_alloc(trans, root->fs_info->extent_root,
                               2 * 1024 * 1024,
@@ -2347,6 +2370,9 @@ static void wait_for_more_refs(struct btrfs_delayed_ref_root *delayed_refs,
  * 0, which means to process everything in the tree at the start
  * of the run (but not newly added entries), or it can be some target
  * number you'd like to process.
+ *
+ * Returns 0 on success or if called with an aborted transaction
+ * Returns <0 on error and aborts the transaction
  */
 int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root, unsigned long count)
@@ -2362,6 +2388,10 @@ int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
        unsigned long num_refs = 0;
        int consider_waiting;
 
+       /* We'll clean this up in btrfs_cleanup_transaction */
+       if (trans->aborted)
+               return 0;
+
        if (root == root->fs_info->extent_root)
                root = root->fs_info->tree_root;
 
@@ -2419,7 +2449,11 @@ again:
                }
 
                ret = run_clustered_refs(trans, root, &cluster);
-               BUG_ON(ret < 0);
+               if (ret < 0) {
+                       spin_unlock(&delayed_refs->lock);
+                       btrfs_abort_transaction(trans, root, ret);
+                       return ret;
+               }
 
                count -= min_t(unsigned long, ret, count);
 
@@ -2584,7 +2618,7 @@ static noinline int check_committed_ref(struct btrfs_trans_handle *trans,
        ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
        if (ret < 0)
                goto out;
-       BUG_ON(ret == 0);
+       BUG_ON(ret == 0); /* Corruption */
 
        ret = -ENOENT;
        if (path->slots[0] == 0)
@@ -2738,7 +2772,6 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
        }
        return 0;
 fail:
-       BUG();
        return ret;
 }
 
@@ -2767,7 +2800,7 @@ static int write_one_cache_group(struct btrfs_trans_handle *trans,
        ret = btrfs_search_slot(trans, extent_root, &cache->key, path, 0, 1);
        if (ret < 0)
                goto fail;
-       BUG_ON(ret);
+       BUG_ON(ret); /* Corruption */
 
        leaf = path->nodes[0];
        bi = btrfs_item_ptr_offset(leaf, path->slots[0]);
@@ -2775,8 +2808,10 @@ static int write_one_cache_group(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(leaf);
        btrfs_release_path(path);
 fail:
-       if (ret)
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
                return ret;
+       }
        return 0;
 
 }
@@ -2949,7 +2984,8 @@ again:
                if (last == 0) {
                        err = btrfs_run_delayed_refs(trans, root,
                                                     (unsigned long)-1);
-                       BUG_ON(err);
+                       if (err) /* File system offline */
+                               goto out;
                }
 
                cache = btrfs_lookup_first_block_group(root->fs_info, last);
@@ -2976,7 +3012,9 @@ again:
                last = cache->key.objectid + cache->key.offset;
 
                err = write_one_cache_group(trans, root, path, cache);
-               BUG_ON(err);
+               if (err) /* File system offline */
+                       goto out;
+
                btrfs_put_block_group(cache);
        }
 
@@ -2989,7 +3027,8 @@ again:
                if (last == 0) {
                        err = btrfs_run_delayed_refs(trans, root,
                                                     (unsigned long)-1);
-                       BUG_ON(err);
+                       if (err) /* File system offline */
+                               goto out;
                }
 
                cache = btrfs_lookup_first_block_group(root->fs_info, last);
@@ -3014,20 +3053,21 @@ again:
                        continue;
                }
 
-               btrfs_write_out_cache(root, trans, cache, path);
+               err = btrfs_write_out_cache(root, trans, cache, path);
 
                /*
                 * If we didn't have an error then the cache state is still
                 * NEED_WRITE, so we can set it to WRITTEN.
                 */
-               if (cache->disk_cache_state == BTRFS_DC_NEED_WRITE)
+               if (!err && cache->disk_cache_state == BTRFS_DC_NEED_WRITE)
                        cache->disk_cache_state = BTRFS_DC_WRITTEN;
                last = cache->key.objectid + cache->key.offset;
                btrfs_put_block_group(cache);
        }
+out:
 
        btrfs_free_path(path);
-       return 0;
+       return err;
 }
 
 int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr)
@@ -3098,11 +3138,8 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
 
 static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
 {
-       u64 extra_flags = flags & BTRFS_BLOCK_GROUP_PROFILE_MASK;
-
-       /* chunk -> extended profile */
-       if (extra_flags == 0)
-               extra_flags = BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+       u64 extra_flags = chunk_to_extended(flags) &
+                               BTRFS_EXTENDED_PROFILE_MASK;
 
        if (flags & BTRFS_BLOCK_GROUP_DATA)
                fs_info->avail_data_alloc_bits |= extra_flags;
@@ -3112,6 +3149,35 @@ static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
                fs_info->avail_system_alloc_bits |= extra_flags;
 }
 
+/*
+ * returns target flags in extended format or 0 if restripe for this
+ * chunk_type is not in progress
+ */
+static u64 get_restripe_target(struct btrfs_fs_info *fs_info, u64 flags)
+{
+       struct btrfs_balance_control *bctl = fs_info->balance_ctl;
+       u64 target = 0;
+
+       BUG_ON(!mutex_is_locked(&fs_info->volume_mutex) &&
+              !spin_is_locked(&fs_info->balance_lock));
+
+       if (!bctl)
+               return 0;
+
+       if (flags & BTRFS_BLOCK_GROUP_DATA &&
+           bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) {
+               target = BTRFS_BLOCK_GROUP_DATA | bctl->data.target;
+       } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM &&
+                  bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) {
+               target = BTRFS_BLOCK_GROUP_SYSTEM | bctl->sys.target;
+       } else if (flags & BTRFS_BLOCK_GROUP_METADATA &&
+                  bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) {
+               target = BTRFS_BLOCK_GROUP_METADATA | bctl->meta.target;
+       }
+
+       return target;
+}
+
 /*
  * @flags: available profiles in extended format (see ctree.h)
  *
@@ -3128,31 +3194,19 @@ u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags)
         */
        u64 num_devices = root->fs_info->fs_devices->rw_devices +
                root->fs_info->fs_devices->missing_devices;
+       u64 target;
 
-       /* pick restriper's target profile if it's available */
+       /*
+        * see if restripe for this chunk_type is in progress, if so
+        * try to reduce to the target profile
+        */
        spin_lock(&root->fs_info->balance_lock);
-       if (root->fs_info->balance_ctl) {
-               struct btrfs_balance_control *bctl = root->fs_info->balance_ctl;
-               u64 tgt = 0;
-
-               if ((flags & BTRFS_BLOCK_GROUP_DATA) &&
-                   (bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
-                   (flags & bctl->data.target)) {
-                       tgt = BTRFS_BLOCK_GROUP_DATA | bctl->data.target;
-               } else if ((flags & BTRFS_BLOCK_GROUP_SYSTEM) &&
-                          (bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
-                          (flags & bctl->sys.target)) {
-                       tgt = BTRFS_BLOCK_GROUP_SYSTEM | bctl->sys.target;
-               } else if ((flags & BTRFS_BLOCK_GROUP_METADATA) &&
-                          (bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
-                          (flags & bctl->meta.target)) {
-                       tgt = BTRFS_BLOCK_GROUP_METADATA | bctl->meta.target;
-               }
-
-               if (tgt) {
+       target = get_restripe_target(root->fs_info, flags);
+       if (target) {
+               /* pick target profile only if it's already available */
+               if ((flags & target) & BTRFS_EXTENDED_PROFILE_MASK) {
                        spin_unlock(&root->fs_info->balance_lock);
-                       flags = tgt;
-                       goto out;
+                       return extended_to_chunk(target);
                }
        }
        spin_unlock(&root->fs_info->balance_lock);
@@ -3180,10 +3234,7 @@ u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags)
                flags &= ~BTRFS_BLOCK_GROUP_RAID0;
        }
 
-out:
-       /* extended -> chunk profile */
-       flags &= ~BTRFS_AVAIL_ALLOC_BIT_SINGLE;
-       return flags;
+       return extended_to_chunk(flags);
 }
 
 static u64 get_alloc_profile(struct btrfs_root *root, u64 flags)
@@ -3312,8 +3363,7 @@ commit_trans:
        }
        data_sinfo->bytes_may_use += bytes;
        trace_btrfs_space_reservation(root->fs_info, "space_info",
-                                     (u64)(unsigned long)data_sinfo,
-                                     bytes, 1);
+                                     data_sinfo->flags, bytes, 1);
        spin_unlock(&data_sinfo->lock);
 
        return 0;
@@ -3334,8 +3384,7 @@ void btrfs_free_reserved_data_space(struct inode *inode, u64 bytes)
        spin_lock(&data_sinfo->lock);
        data_sinfo->bytes_may_use -= bytes;
        trace_btrfs_space_reservation(root->fs_info, "space_info",
-                                     (u64)(unsigned long)data_sinfo,
-                                     bytes, 0);
+                                     data_sinfo->flags, bytes, 0);
        spin_unlock(&data_sinfo->lock);
 }
 
@@ -3396,6 +3445,50 @@ static int should_alloc_chunk(struct btrfs_root *root,
        return 1;
 }
 
+static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type)
+{
+       u64 num_dev;
+
+       if (type & BTRFS_BLOCK_GROUP_RAID10 ||
+           type & BTRFS_BLOCK_GROUP_RAID0)
+               num_dev = root->fs_info->fs_devices->rw_devices;
+       else if (type & BTRFS_BLOCK_GROUP_RAID1)
+               num_dev = 2;
+       else
+               num_dev = 1;    /* DUP or single */
+
+       /* metadata for updaing devices and chunk tree */
+       return btrfs_calc_trans_metadata_size(root, num_dev + 1);
+}
+
+static void check_system_chunk(struct btrfs_trans_handle *trans,
+                              struct btrfs_root *root, u64 type)
+{
+       struct btrfs_space_info *info;
+       u64 left;
+       u64 thresh;
+
+       info = __find_space_info(root->fs_info, BTRFS_BLOCK_GROUP_SYSTEM);
+       spin_lock(&info->lock);
+       left = info->total_bytes - info->bytes_used - info->bytes_pinned -
+               info->bytes_reserved - info->bytes_readonly;
+       spin_unlock(&info->lock);
+
+       thresh = get_system_chunk_thresh(root, type);
+       if (left < thresh && btrfs_test_opt(root, ENOSPC_DEBUG)) {
+               printk(KERN_INFO "left=%llu, need=%llu, flags=%llu\n",
+                      left, thresh, type);
+               dump_space_info(info, 0, 0);
+       }
+
+       if (left < thresh) {
+               u64 flags;
+
+               flags = btrfs_get_alloc_profile(root->fs_info->chunk_root, 0);
+               btrfs_alloc_chunk(trans, root, flags);
+       }
+}
+
 static int do_chunk_alloc(struct btrfs_trans_handle *trans,
                          struct btrfs_root *extent_root, u64 alloc_bytes,
                          u64 flags, int force)
@@ -3405,15 +3498,13 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
        int wait_for_alloc = 0;
        int ret = 0;
 
-       BUG_ON(!profile_is_valid(flags, 0));
-
        space_info = __find_space_info(extent_root->fs_info, flags);
        if (!space_info) {
                ret = update_space_info(extent_root->fs_info, flags,
                                        0, 0, &space_info);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
-       BUG_ON(!space_info);
+       BUG_ON(!space_info); /* Logic error */
 
 again:
        spin_lock(&space_info->lock);
@@ -3468,6 +3559,12 @@ again:
                        force_metadata_allocation(fs_info);
        }
 
+       /*
+        * Check if we have enough space in SYSTEM chunk because we may need
+        * to update devices.
+        */
+       check_system_chunk(trans, extent_root, flags);
+
        ret = btrfs_alloc_chunk(trans, extent_root, flags);
        if (ret < 0 && ret != -ENOSPC)
                goto out;
@@ -3678,8 +3775,10 @@ again:
                ret = wait_event_interruptible(space_info->wait,
                                               !space_info->flush);
                /* Must have been interrupted, return */
-               if (ret)
+               if (ret) {
+                       printk(KERN_DEBUG "btrfs: %s returning -EINTR\n", __func__);
                        return -EINTR;
+               }
 
                spin_lock(&space_info->lock);
        }
@@ -3700,9 +3799,7 @@ again:
                if (used + orig_bytes <= space_info->total_bytes) {
                        space_info->bytes_may_use += orig_bytes;
                        trace_btrfs_space_reservation(root->fs_info,
-                                             "space_info",
-                                             (u64)(unsigned long)space_info,
-                                             orig_bytes, 1);
+                               "space_info", space_info->flags, orig_bytes, 1);
                        ret = 0;
                } else {
                        /*
@@ -3771,9 +3868,7 @@ again:
                if (used + num_bytes < space_info->total_bytes + avail) {
                        space_info->bytes_may_use += orig_bytes;
                        trace_btrfs_space_reservation(root->fs_info,
-                                             "space_info",
-                                             (u64)(unsigned long)space_info,
-                                             orig_bytes, 1);
+                               "space_info", space_info->flags, orig_bytes, 1);
                        ret = 0;
                } else {
                        wait_ordered = true;
@@ -3836,8 +3931,9 @@ out:
        return ret;
 }
 
-static struct btrfs_block_rsv *get_block_rsv(struct btrfs_trans_handle *trans,
-                                            struct btrfs_root *root)
+static struct btrfs_block_rsv *get_block_rsv(
+                                       const struct btrfs_trans_handle *trans,
+                                       const struct btrfs_root *root)
 {
        struct btrfs_block_rsv *block_rsv = NULL;
 
@@ -3918,8 +4014,7 @@ static void block_rsv_release_bytes(struct btrfs_fs_info *fs_info,
                        spin_lock(&space_info->lock);
                        space_info->bytes_may_use -= num_bytes;
                        trace_btrfs_space_reservation(fs_info, "space_info",
-                                             (u64)(unsigned long)space_info,
-                                             num_bytes, 0);
+                                       space_info->flags, num_bytes, 0);
                        space_info->reservation_progress++;
                        spin_unlock(&space_info->lock);
                }
@@ -4137,14 +4232,14 @@ static void update_global_block_rsv(struct btrfs_fs_info *fs_info)
                block_rsv->reserved += num_bytes;
                sinfo->bytes_may_use += num_bytes;
                trace_btrfs_space_reservation(fs_info, "space_info",
-                                     (u64)(unsigned long)sinfo, num_bytes, 1);
+                                     sinfo->flags, num_bytes, 1);
        }
 
        if (block_rsv->reserved >= block_rsv->size) {
                num_bytes = block_rsv->reserved - block_rsv->size;
                sinfo->bytes_may_use -= num_bytes;
                trace_btrfs_space_reservation(fs_info, "space_info",
-                                     (u64)(unsigned long)sinfo, num_bytes, 0);
+                                     sinfo->flags, num_bytes, 0);
                sinfo->reservation_progress++;
                block_rsv->reserved = block_rsv->size;
                block_rsv->full = 1;
@@ -4198,12 +4293,12 @@ void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans,
                return;
 
        trace_btrfs_space_reservation(root->fs_info, "transaction",
-                                     (u64)(unsigned long)trans,
-                                     trans->bytes_reserved, 0);
+                                     trans->transid, trans->bytes_reserved, 0);
        btrfs_block_rsv_release(root, trans->block_rsv, trans->bytes_reserved);
        trans->bytes_reserved = 0;
 }
 
+/* Can only return 0 or -ENOSPC */
 int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans,
                                  struct inode *inode)
 {
@@ -4540,7 +4635,7 @@ static int update_block_group(struct btrfs_trans_handle *trans,
        while (total) {
                cache = btrfs_lookup_block_group(info, bytenr);
                if (!cache)
-                       return -1;
+                       return -ENOENT;
                if (cache->flags & (BTRFS_BLOCK_GROUP_DUP |
                                    BTRFS_BLOCK_GROUP_RAID1 |
                                    BTRFS_BLOCK_GROUP_RAID10))
@@ -4643,7 +4738,7 @@ int btrfs_pin_extent(struct btrfs_root *root,
        struct btrfs_block_group_cache *cache;
 
        cache = btrfs_lookup_block_group(root->fs_info, bytenr);
-       BUG_ON(!cache);
+       BUG_ON(!cache); /* Logic error */
 
        pin_down_extent(root, cache, bytenr, num_bytes, reserved);
 
@@ -4661,7 +4756,7 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_trans_handle *trans,
        struct btrfs_block_group_cache *cache;
 
        cache = btrfs_lookup_block_group(root->fs_info, bytenr);
-       BUG_ON(!cache);
+       BUG_ON(!cache); /* Logic error */
 
        /*
         * pull in the free space cache (if any) so that our pin
@@ -4706,6 +4801,7 @@ static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache,
 {
        struct btrfs_space_info *space_info = cache->space_info;
        int ret = 0;
+
        spin_lock(&space_info->lock);
        spin_lock(&cache->lock);
        if (reserve != RESERVE_FREE) {
@@ -4716,9 +4812,8 @@ static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache,
                        space_info->bytes_reserved += num_bytes;
                        if (reserve == RESERVE_ALLOC) {
                                trace_btrfs_space_reservation(cache->fs_info,
-                                             "space_info",
-                                             (u64)(unsigned long)space_info,
-                                             num_bytes, 0);
+                                               "space_info", space_info->flags,
+                                               num_bytes, 0);
                                space_info->bytes_may_use -= num_bytes;
                        }
                }
@@ -4734,7 +4829,7 @@ static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache,
        return ret;
 }
 
-int btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
+void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -4764,7 +4859,6 @@ int btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
        up_write(&fs_info->extent_commit_sem);
 
        update_global_block_rsv(fs_info);
-       return 0;
 }
 
 static int unpin_extent_range(struct btrfs_root *root, u64 start, u64 end)
@@ -4779,7 +4873,7 @@ static int unpin_extent_range(struct btrfs_root *root, u64 start, u64 end)
                        if (cache)
                                btrfs_put_block_group(cache);
                        cache = btrfs_lookup_block_group(fs_info, start);
-                       BUG_ON(!cache);
+                       BUG_ON(!cache); /* Logic error */
                }
 
                len = cache->key.objectid + cache->key.offset - start;
@@ -4816,6 +4910,9 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
        u64 end;
        int ret;
 
+       if (trans->aborted)
+               return 0;
+
        if (fs_info->pinned_extents == &fs_info->freed_extents[0])
                unpin = &fs_info->freed_extents[1];
        else
@@ -4901,7 +4998,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                        ret = remove_extent_backref(trans, extent_root, path,
                                                    NULL, refs_to_drop,
                                                    is_data);
-                       BUG_ON(ret);
+                       if (ret)
+                               goto abort;
                        btrfs_release_path(path);
                        path->leave_spinning = 1;
 
@@ -4919,10 +5017,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                        btrfs_print_leaf(extent_root,
                                                         path->nodes[0]);
                        }
-                       BUG_ON(ret);
+                       if (ret < 0)
+                               goto abort;
                        extent_slot = path->slots[0];
                }
-       } else {
+       } else if (ret == -ENOENT) {
                btrfs_print_leaf(extent_root, path->nodes[0]);
                WARN_ON(1);
                printk(KERN_ERR "btrfs unable to find ref byte nr %llu "
@@ -4932,6 +5031,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                       (unsigned long long)root_objectid,
                       (unsigned long long)owner_objectid,
                       (unsigned long long)owner_offset);
+       } else {
+               goto abort;
        }
 
        leaf = path->nodes[0];
@@ -4941,7 +5042,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                BUG_ON(found_extent || extent_slot != path->slots[0]);
                ret = convert_extent_item_v0(trans, extent_root, path,
                                             owner_objectid, 0);
-               BUG_ON(ret < 0);
+               if (ret < 0)
+                       goto abort;
 
                btrfs_release_path(path);
                path->leave_spinning = 1;
@@ -4958,7 +5060,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                               (unsigned long long)bytenr);
                        btrfs_print_leaf(extent_root, path->nodes[0]);
                }
-               BUG_ON(ret);
+               if (ret < 0)
+                       goto abort;
                extent_slot = path->slots[0];
                leaf = path->nodes[0];
                item_size = btrfs_item_size_nr(leaf, extent_slot);
@@ -4995,7 +5098,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                        ret = remove_extent_backref(trans, extent_root, path,
                                                    iref, refs_to_drop,
                                                    is_data);
-                       BUG_ON(ret);
+                       if (ret)
+                               goto abort;
                }
        } else {
                if (found_extent) {
@@ -5012,23 +5116,27 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
 
                ret = btrfs_del_items(trans, extent_root, path, path->slots[0],
                                      num_to_del);
-               BUG_ON(ret);
+               if (ret)
+                       goto abort;
                btrfs_release_path(path);
 
                if (is_data) {
                        ret = btrfs_del_csums(trans, root, bytenr, num_bytes);
-                       BUG_ON(ret);
-               } else {
-                       invalidate_mapping_pages(info->btree_inode->i_mapping,
-                            bytenr >> PAGE_CACHE_SHIFT,
-                            (bytenr + num_bytes - 1) >> PAGE_CACHE_SHIFT);
+                       if (ret)
+                               goto abort;
                }
 
                ret = update_block_group(trans, root, bytenr, num_bytes, 0);
-               BUG_ON(ret);
+               if (ret)
+                       goto abort;
        }
+out:
        btrfs_free_path(path);
        return ret;
+
+abort:
+       btrfs_abort_transaction(trans, extent_root, ret);
+       goto out;
 }
 
 /*
@@ -5124,7 +5232,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
                                        parent, root->root_key.objectid,
                                        btrfs_header_level(buf),
                                        BTRFS_DROP_DELAYED_REF, NULL, for_cow);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
 
        if (!last_ref)
@@ -5158,6 +5266,7 @@ out:
        btrfs_put_block_group(cache);
 }
 
+/* Can return -ENOMEM */
 int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                      u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid,
                      u64 owner, u64 offset, int for_cow)
@@ -5179,14 +5288,12 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                                        num_bytes,
                                        parent, root_objectid, (int)owner,
                                        BTRFS_DROP_DELAYED_REF, NULL, for_cow);
-               BUG_ON(ret);
        } else {
                ret = btrfs_add_delayed_data_ref(fs_info, trans, bytenr,
                                                num_bytes,
                                                parent, root_objectid, owner,
                                                offset, BTRFS_DROP_DELAYED_REF,
                                                NULL, for_cow);
-               BUG_ON(ret);
        }
        return ret;
 }
@@ -5243,28 +5350,34 @@ wait_block_group_cache_done(struct btrfs_block_group_cache *cache)
        return 0;
 }
 
-static int get_block_group_index(struct btrfs_block_group_cache *cache)
+static int __get_block_group_index(u64 flags)
 {
        int index;
-       if (cache->flags & BTRFS_BLOCK_GROUP_RAID10)
+
+       if (flags & BTRFS_BLOCK_GROUP_RAID10)
                index = 0;
-       else if (cache->flags & BTRFS_BLOCK_GROUP_RAID1)
+       else if (flags & BTRFS_BLOCK_GROUP_RAID1)
                index = 1;
-       else if (cache->flags & BTRFS_BLOCK_GROUP_DUP)
+       else if (flags & BTRFS_BLOCK_GROUP_DUP)
                index = 2;
-       else if (cache->flags & BTRFS_BLOCK_GROUP_RAID0)
+       else if (flags & BTRFS_BLOCK_GROUP_RAID0)
                index = 3;
        else
                index = 4;
+
        return index;
 }
 
+static int get_block_group_index(struct btrfs_block_group_cache *cache)
+{
+       return __get_block_group_index(cache->flags);
+}
+
 enum btrfs_loop_type {
-       LOOP_FIND_IDEAL = 0,
-       LOOP_CACHING_NOWAIT = 1,
-       LOOP_CACHING_WAIT = 2,
-       LOOP_ALLOC_CHUNK = 3,
-       LOOP_NO_EMPTY_SIZE = 4,
+       LOOP_CACHING_NOWAIT = 0,
+       LOOP_CACHING_WAIT = 1,
+       LOOP_ALLOC_CHUNK = 2,
+       LOOP_NO_EMPTY_SIZE = 3,
 };
 
 /*
@@ -5278,7 +5391,6 @@ enum btrfs_loop_type {
 static noinline int find_free_extent(struct btrfs_trans_handle *trans,
                                     struct btrfs_root *orig_root,
                                     u64 num_bytes, u64 empty_size,
-                                    u64 search_start, u64 search_end,
                                     u64 hint_byte, struct btrfs_key *ins,
                                     u64 data)
 {
@@ -5287,6 +5399,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
        struct btrfs_free_cluster *last_ptr = NULL;
        struct btrfs_block_group_cache *block_group = NULL;
        struct btrfs_block_group_cache *used_block_group;
+       u64 search_start = 0;
        int empty_cluster = 2 * 1024 * 1024;
        int allowed_chunk_alloc = 0;
        int done_chunk_alloc = 0;
@@ -5300,8 +5413,6 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
        bool failed_alloc = false;
        bool use_cluster = true;
        bool have_caching_bg = false;
-       u64 ideal_cache_percent = 0;
-       u64 ideal_cache_offset = 0;
 
        WARN_ON(num_bytes < root->sectorsize);
        btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
@@ -5351,7 +5462,6 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
                empty_cluster = 0;
 
        if (search_start == hint_byte) {
-ideal_cache:
                block_group = btrfs_lookup_block_group(root->fs_info,
                                                       search_start);
                used_block_group = block_group;
@@ -5363,8 +5473,7 @@ ideal_cache:
                 * picked out then we don't care that the block group is cached.
                 */
                if (block_group && block_group_bits(block_group, data) &&
-                   (block_group->cached != BTRFS_CACHE_NO ||
-                    search_start == ideal_cache_offset)) {
+                   block_group->cached != BTRFS_CACHE_NO) {
                        down_read(&space_info->groups_sem);
                        if (list_empty(&block_group->list) ||
                            block_group->ro) {
@@ -5418,44 +5527,13 @@ search:
 have_block_group:
                cached = block_group_cache_done(block_group);
                if (unlikely(!cached)) {
-                       u64 free_percent;
-
                        found_uncached_bg = true;
                        ret = cache_block_group(block_group, trans,
-                                               orig_root, 1);
-                       if (block_group->cached == BTRFS_CACHE_FINISHED)
-                               goto alloc;
-
-                       free_percent = btrfs_block_group_used(&block_group->item);
-                       free_percent *= 100;
-                       free_percent = div64_u64(free_percent,
-                                                block_group->key.offset);
-                       free_percent = 100 - free_percent;
-                       if (free_percent > ideal_cache_percent &&
-                           likely(!block_group->ro)) {
-                               ideal_cache_offset = block_group->key.objectid;
-                               ideal_cache_percent = free_percent;
-                       }
-
-                       /*
-                        * The caching workers are limited to 2 threads, so we
-                        * can queue as much work as we care to.
-                        */
-                       if (loop > LOOP_FIND_IDEAL) {
-                               ret = cache_block_group(block_group, trans,
-                                                       orig_root, 0);
-                               BUG_ON(ret);
-                       }
-
-                       /*
-                        * If loop is set for cached only, try the next block
-                        * group.
-                        */
-                       if (loop == LOOP_FIND_IDEAL)
-                               goto loop;
+                                               orig_root, 0);
+                       BUG_ON(ret < 0);
+                       ret = 0;
                }
 
-alloc:
                if (unlikely(block_group->ro))
                        goto loop;
 
@@ -5606,11 +5684,6 @@ unclustered_alloc:
                }
 checks:
                search_start = stripe_align(root, offset);
-               /* move on to the next group */
-               if (search_start + num_bytes >= search_end) {
-                       btrfs_add_free_space(used_block_group, offset, num_bytes);
-                       goto loop;
-               }
 
                /* move on to the next group */
                if (search_start + num_bytes >
@@ -5661,9 +5734,7 @@ loop:
        if (!ins->objectid && ++index < BTRFS_NR_RAID_TYPES)
                goto search;
 
-       /* LOOP_FIND_IDEAL, only search caching/cached bg's, and don't wait for
-        *                      for them to make caching progress.  Also
-        *                      determine the best possible bg to cache
+       /*
         * LOOP_CACHING_NOWAIT, search partially cached block groups, kicking
         *                      caching kthreads as we move along
         * LOOP_CACHING_WAIT, search everything, and wait if our bg is caching
@@ -5673,50 +5744,17 @@ loop:
         */
        if (!ins->objectid && loop < LOOP_NO_EMPTY_SIZE) {
                index = 0;
-               if (loop == LOOP_FIND_IDEAL && found_uncached_bg) {
-                       found_uncached_bg = false;
-                       loop++;
-                       if (!ideal_cache_percent)
-                               goto search;
-
-                       /*
-                        * 1 of the following 2 things have happened so far
-                        *
-                        * 1) We found an ideal block group for caching that
-                        * is mostly full and will cache quickly, so we might
-                        * as well wait for it.
-                        *
-                        * 2) We searched for cached only and we didn't find
-                        * anything, and we didn't start any caching kthreads
-                        * either, so chances are we will loop through and
-                        * start a couple caching kthreads, and then come back
-                        * around and just wait for them.  This will be slower
-                        * because we will have 2 caching kthreads reading at
-                        * the same time when we could have just started one
-                        * and waited for it to get far enough to give us an
-                        * allocation, so go ahead and go to the wait caching
-                        * loop.
-                        */
-                       loop = LOOP_CACHING_WAIT;
-                       search_start = ideal_cache_offset;
-                       ideal_cache_percent = 0;
-                       goto ideal_cache;
-               } else if (loop == LOOP_FIND_IDEAL) {
-                       /*
-                        * Didn't find a uncached bg, wait on anything we find
-                        * next.
-                        */
-                       loop = LOOP_CACHING_WAIT;
-                       goto search;
-               }
-
                loop++;
-
                if (loop == LOOP_ALLOC_CHUNK) {
                       if (allowed_chunk_alloc) {
                                ret = do_chunk_alloc(trans, root, num_bytes +
                                                     2 * 1024 * 1024, data,
                                                     CHUNK_ALLOC_LIMITED);
+                               if (ret < 0) {
+                                       btrfs_abort_transaction(trans,
+                                                               root, ret);
+                                       goto out;
+                               }
                                allowed_chunk_alloc = 0;
                                if (ret == 1)
                                        done_chunk_alloc = 1;
@@ -5745,6 +5783,7 @@ loop:
        } else if (ins->objectid) {
                ret = 0;
        }
+out:
 
        return ret;
 }
@@ -5798,12 +5837,10 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
                         struct btrfs_root *root,
                         u64 num_bytes, u64 min_alloc_size,
                         u64 empty_size, u64 hint_byte,
-                        u64 search_end, struct btrfs_key *ins,
-                        u64 data)
+                        struct btrfs_key *ins, u64 data)
 {
        bool final_tried = false;
        int ret;
-       u64 search_start = 0;
 
        data = btrfs_get_alloc_profile(root, data);
 again:
@@ -5811,23 +5848,31 @@ again:
         * the only place that sets empty_size is btrfs_realloc_node, which
         * is not called recursively on allocations
         */
-       if (empty_size || root->ref_cows)
+       if (empty_size || root->ref_cows) {
                ret = do_chunk_alloc(trans, root->fs_info->extent_root,
                                     num_bytes + 2 * 1024 * 1024, data,
                                     CHUNK_ALLOC_NO_FORCE);
+               if (ret < 0 && ret != -ENOSPC) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       return ret;
+               }
+       }
 
        WARN_ON(num_bytes < root->sectorsize);
        ret = find_free_extent(trans, root, num_bytes, empty_size,
-                              search_start, search_end, hint_byte,
-                              ins, data);
+                              hint_byte, ins, data);
 
        if (ret == -ENOSPC) {
                if (!final_tried) {
                        num_bytes = num_bytes >> 1;
                        num_bytes = num_bytes & ~(root->sectorsize - 1);
                        num_bytes = max(num_bytes, min_alloc_size);
-                       do_chunk_alloc(trans, root->fs_info->extent_root,
+                       ret = do_chunk_alloc(trans, root->fs_info->extent_root,
                                       num_bytes, data, CHUNK_ALLOC_FORCE);
+                       if (ret < 0 && ret != -ENOSPC) {
+                               btrfs_abort_transaction(trans, root, ret);
+                               return ret;
+                       }
                        if (num_bytes == min_alloc_size)
                                final_tried = true;
                        goto again;
@@ -5838,7 +5883,8 @@ again:
                        printk(KERN_ERR "btrfs allocation failed flags %llu, "
                               "wanted %llu\n", (unsigned long long)data,
                               (unsigned long long)num_bytes);
-                       dump_space_info(sinfo, num_bytes, 1);
+                       if (sinfo)
+                               dump_space_info(sinfo, num_bytes, 1);
                }
        }
 
@@ -5917,7 +5963,10 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
        path->leave_spinning = 1;
        ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
                                      ins, size);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_free_path(path);
+               return ret;
+       }
 
        leaf = path->nodes[0];
        extent_item = btrfs_item_ptr(leaf, path->slots[0],
@@ -5947,7 +5996,7 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
        btrfs_free_path(path);
 
        ret = update_block_group(trans, root, ins->objectid, ins->offset, 1);
-       if (ret) {
+       if (ret) { /* -ENOENT, logic error */
                printk(KERN_ERR "btrfs update block group failed for %llu "
                       "%llu\n", (unsigned long long)ins->objectid,
                       (unsigned long long)ins->offset);
@@ -5978,7 +6027,10 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        path->leave_spinning = 1;
        ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
                                      ins, size);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_free_path(path);
+               return ret;
+       }
 
        leaf = path->nodes[0];
        extent_item = btrfs_item_ptr(leaf, path->slots[0],
@@ -6008,7 +6060,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        btrfs_free_path(path);
 
        ret = update_block_group(trans, root, ins->objectid, ins->offset, 1);
-       if (ret) {
+       if (ret) { /* -ENOENT, logic error */
                printk(KERN_ERR "btrfs update block group failed for %llu "
                       "%llu\n", (unsigned long long)ins->objectid,
                       (unsigned long long)ins->offset);
@@ -6056,28 +6108,28 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
        if (!caching_ctl) {
                BUG_ON(!block_group_cache_done(block_group));
                ret = btrfs_remove_free_space(block_group, start, num_bytes);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        } else {
                mutex_lock(&caching_ctl->mutex);
 
                if (start >= caching_ctl->progress) {
                        ret = add_excluded_extent(root, start, num_bytes);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
                } else if (start + num_bytes <= caching_ctl->progress) {
                        ret = btrfs_remove_free_space(block_group,
                                                      start, num_bytes);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
                } else {
                        num_bytes = caching_ctl->progress - start;
                        ret = btrfs_remove_free_space(block_group,
                                                      start, num_bytes);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
 
                        start = caching_ctl->progress;
                        num_bytes = ins->objectid + ins->offset -
                                    caching_ctl->progress;
                        ret = add_excluded_extent(root, start, num_bytes);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
                }
 
                mutex_unlock(&caching_ctl->mutex);
@@ -6086,7 +6138,7 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
 
        ret = btrfs_update_reserved_bytes(block_group, ins->offset,
                                          RESERVE_ALLOC_NO_ACCOUNT);
-       BUG_ON(ret);
+       BUG_ON(ret); /* logic error */
        btrfs_put_block_group(block_group);
        ret = alloc_reserved_file_extent(trans, root, 0, root_objectid,
                                         0, owner, offset, ins, 1);
@@ -6107,6 +6159,7 @@ struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans,
        btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level);
        btrfs_tree_lock(buf);
        clean_tree_block(trans, root, buf);
+       clear_bit(EXTENT_BUFFER_STALE, &buf->bflags);
 
        btrfs_set_lock_blocking(buf);
        btrfs_set_buffer_uptodate(buf);
@@ -6214,7 +6267,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
                return ERR_CAST(block_rsv);
 
        ret = btrfs_reserve_extent(trans, root, blocksize, blocksize,
-                                  empty_size, hint, (u64)-1, &ins, 0);
+                                  empty_size, hint, &ins, 0);
        if (ret) {
                unuse_block_rsv(root->fs_info, block_rsv, blocksize);
                return ERR_PTR(ret);
@@ -6222,7 +6275,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
 
        buf = btrfs_init_new_buffer(trans, root, ins.objectid,
                                    blocksize, level);
-       BUG_ON(IS_ERR(buf));
+       BUG_ON(IS_ERR(buf)); /* -ENOMEM */
 
        if (root_objectid == BTRFS_TREE_RELOC_OBJECTID) {
                if (parent == 0)
@@ -6234,7 +6287,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
        if (root_objectid != BTRFS_TREE_LOG_OBJECTID) {
                struct btrfs_delayed_extent_op *extent_op;
                extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS);
-               BUG_ON(!extent_op);
+               BUG_ON(!extent_op); /* -ENOMEM */
                if (key)
                        memcpy(&extent_op->key, key, sizeof(extent_op->key));
                else
@@ -6249,7 +6302,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
                                        ins.offset, parent, root_objectid,
                                        level, BTRFS_ADD_DELAYED_EXTENT,
                                        extent_op, for_cow);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
        return buf;
 }
@@ -6319,7 +6372,9 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans,
                /* We don't lock the tree block, it's OK to be racy here */
                ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize,
                                               &refs, &flags);
-               BUG_ON(ret);
+               /* We don't care about errors in readahead. */
+               if (ret < 0)
+                       continue;
                BUG_ON(refs == 0);
 
                if (wc->stage == DROP_REFERENCE) {
@@ -6386,7 +6441,9 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
                                               eb->start, eb->len,
                                               &wc->refs[level],
                                               &wc->flags[level]);
-               BUG_ON(ret);
+               BUG_ON(ret == -ENOMEM);
+               if (ret)
+                       return ret;
                BUG_ON(wc->refs[level] == 0);
        }
 
@@ -6405,12 +6462,12 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
        if (!(wc->flags[level] & flag)) {
                BUG_ON(!path->locks[level]);
                ret = btrfs_inc_ref(trans, root, eb, 1, wc->for_reloc);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
                ret = btrfs_dec_ref(trans, root, eb, 0, wc->for_reloc);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
                ret = btrfs_set_disk_extent_flags(trans, root, eb->start,
                                                  eb->len, flag, 0);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
                wc->flags[level] |= flag;
        }
 
@@ -6482,7 +6539,11 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
        ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize,
                                       &wc->refs[level - 1],
                                       &wc->flags[level - 1]);
-       BUG_ON(ret);
+       if (ret < 0) {
+               btrfs_tree_unlock(next);
+               return ret;
+       }
+
        BUG_ON(wc->refs[level - 1] == 0);
        *lookup_info = 0;
 
@@ -6551,7 +6612,7 @@ skip:
 
                ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent,
                                root->root_key.objectid, level - 1, 0, 0);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
        btrfs_tree_unlock(next);
        free_extent_buffer(next);
@@ -6609,7 +6670,10 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
                                                       eb->start, eb->len,
                                                       &wc->refs[level],
                                                       &wc->flags[level]);
-                       BUG_ON(ret);
+                       if (ret < 0) {
+                               btrfs_tree_unlock_rw(eb, path->locks[level]);
+                               return ret;
+                       }
                        BUG_ON(wc->refs[level] == 0);
                        if (wc->refs[level] == 1) {
                                btrfs_tree_unlock_rw(eb, path->locks[level]);
@@ -6629,7 +6693,7 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
                        else
                                ret = btrfs_dec_ref(trans, root, eb, 0,
                                                    wc->for_reloc);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM */
                }
                /* make block locked assertion in clean_tree_block happy */
                if (!path->locks[level] &&
@@ -6738,7 +6802,7 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
  * also make sure backrefs for the shared block and all lower level
  * blocks are properly updated.
  */
-void btrfs_drop_snapshot(struct btrfs_root *root,
+int btrfs_drop_snapshot(struct btrfs_root *root,
                         struct btrfs_block_rsv *block_rsv, int update_ref,
                         int for_reloc)
 {
@@ -6766,7 +6830,10 @@ void btrfs_drop_snapshot(struct btrfs_root *root,
        }
 
        trans = btrfs_start_transaction(tree_root, 0);
-       BUG_ON(IS_ERR(trans));
+       if (IS_ERR(trans)) {
+               err = PTR_ERR(trans);
+               goto out_free;
+       }
 
        if (block_rsv)
                trans->block_rsv = block_rsv;
@@ -6791,7 +6858,7 @@ void btrfs_drop_snapshot(struct btrfs_root *root,
                path->lowest_level = 0;
                if (ret < 0) {
                        err = ret;
-                       goto out_free;
+                       goto out_end_trans;
                }
                WARN_ON(ret > 0);
 
@@ -6811,7 +6878,10 @@ void btrfs_drop_snapshot(struct btrfs_root *root,
                                                path->nodes[level]->len,
                                                &wc->refs[level],
                                                &wc->flags[level]);
-                       BUG_ON(ret);
+                       if (ret < 0) {
+                               err = ret;
+                               goto out_end_trans;
+                       }
                        BUG_ON(wc->refs[level] == 0);
 
                        if (level == root_item->drop_level)
@@ -6862,26 +6932,40 @@ void btrfs_drop_snapshot(struct btrfs_root *root,
                        ret = btrfs_update_root(trans, tree_root,
                                                &root->root_key,
                                                root_item);
-                       BUG_ON(ret);
+                       if (ret) {
+                               btrfs_abort_transaction(trans, tree_root, ret);
+                               err = ret;
+                               goto out_end_trans;
+                       }
 
                        btrfs_end_transaction_throttle(trans, tree_root);
                        trans = btrfs_start_transaction(tree_root, 0);
-                       BUG_ON(IS_ERR(trans));
+                       if (IS_ERR(trans)) {
+                               err = PTR_ERR(trans);
+                               goto out_free;
+                       }
                        if (block_rsv)
                                trans->block_rsv = block_rsv;
                }
        }
        btrfs_release_path(path);
-       BUG_ON(err);
+       if (err)
+               goto out_end_trans;
 
        ret = btrfs_del_root(trans, tree_root, &root->root_key);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, tree_root, ret);
+               goto out_end_trans;
+       }
 
        if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
                ret = btrfs_find_last_root(tree_root, root->root_key.objectid,
                                           NULL, NULL);
-               BUG_ON(ret < 0);
-               if (ret > 0) {
+               if (ret < 0) {
+                       btrfs_abort_transaction(trans, tree_root, ret);
+                       err = ret;
+                       goto out_end_trans;
+               } else if (ret > 0) {
                        /* if we fail to delete the orphan item this time
                         * around, it'll get picked up the next time.
                         *
@@ -6899,14 +6983,15 @@ void btrfs_drop_snapshot(struct btrfs_root *root,
                free_extent_buffer(root->commit_root);
                kfree(root);
        }
-out_free:
+out_end_trans:
        btrfs_end_transaction_throttle(trans, tree_root);
+out_free:
        kfree(wc);
        btrfs_free_path(path);
 out:
        if (err)
                btrfs_std_error(root->fs_info, err);
-       return;
+       return err;
 }
 
 /*
@@ -6983,31 +7068,15 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
 static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
 {
        u64 num_devices;
-       u64 stripped = BTRFS_BLOCK_GROUP_RAID0 |
-               BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10;
+       u64 stripped;
 
-       if (root->fs_info->balance_ctl) {
-               struct btrfs_balance_control *bctl = root->fs_info->balance_ctl;
-               u64 tgt = 0;
-
-               /* pick restriper's target profile and return */
-               if (flags & BTRFS_BLOCK_GROUP_DATA &&
-                   bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) {
-                       tgt = BTRFS_BLOCK_GROUP_DATA | bctl->data.target;
-               } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM &&
-                          bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) {
-                       tgt = BTRFS_BLOCK_GROUP_SYSTEM | bctl->sys.target;
-               } else if (flags & BTRFS_BLOCK_GROUP_METADATA &&
-                          bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) {
-                       tgt = BTRFS_BLOCK_GROUP_METADATA | bctl->meta.target;
-               }
-
-               if (tgt) {
-                       /* extended -> chunk profile */
-                       tgt &= ~BTRFS_AVAIL_ALLOC_BIT_SINGLE;
-                       return tgt;
-               }
-       }
+       /*
+        * if restripe for this chunk_type is on pick target profile and
+        * return, otherwise do the usual balance
+        */
+       stripped = get_restripe_target(root->fs_info, flags);
+       if (stripped)
+               return extended_to_chunk(stripped);
 
        /*
         * we add in the count of missing devices because we want
@@ -7017,6 +7086,9 @@ static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
        num_devices = root->fs_info->fs_devices->rw_devices +
                root->fs_info->fs_devices->missing_devices;
 
+       stripped = BTRFS_BLOCK_GROUP_RAID0 |
+               BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10;
+
        if (num_devices == 1) {
                stripped |= BTRFS_BLOCK_GROUP_DUP;
                stripped = flags & ~stripped;
@@ -7029,7 +7101,6 @@ static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
                if (flags & (BTRFS_BLOCK_GROUP_RAID1 |
                             BTRFS_BLOCK_GROUP_RAID10))
                        return stripped | BTRFS_BLOCK_GROUP_DUP;
-               return flags;
        } else {
                /* they already had raid on here, just return */
                if (flags & stripped)
@@ -7042,9 +7113,9 @@ static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
                if (flags & BTRFS_BLOCK_GROUP_DUP)
                        return stripped | BTRFS_BLOCK_GROUP_RAID1;
 
-               /* turn single device chunks into raid0 */
-               return stripped | BTRFS_BLOCK_GROUP_RAID0;
+               /* this is drive concat, leave it alone */
        }
+
        return flags;
 }
 
@@ -7103,12 +7174,16 @@ int btrfs_set_block_group_ro(struct btrfs_root *root,
        BUG_ON(cache->ro);
 
        trans = btrfs_join_transaction(root);
-       BUG_ON(IS_ERR(trans));
+       if (IS_ERR(trans))
+               return PTR_ERR(trans);
 
        alloc_flags = update_block_group_flags(root, cache->flags);
-       if (alloc_flags != cache->flags)
-               do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags,
-                              CHUNK_ALLOC_FORCE);
+       if (alloc_flags != cache->flags) {
+               ret = do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags,
+                                    CHUNK_ALLOC_FORCE);
+               if (ret < 0)
+                       goto out;
+       }
 
        ret = set_block_group_ro(cache, 0);
        if (!ret)
@@ -7188,7 +7263,7 @@ u64 btrfs_account_ro_block_groups_free_space(struct btrfs_space_info *sinfo)
        return free_bytes;
 }
 
-int btrfs_set_block_group_rw(struct btrfs_root *root,
+void btrfs_set_block_group_rw(struct btrfs_root *root,
                              struct btrfs_block_group_cache *cache)
 {
        struct btrfs_space_info *sinfo = cache->space_info;
@@ -7204,7 +7279,6 @@ int btrfs_set_block_group_rw(struct btrfs_root *root,
        cache->ro = 0;
        spin_unlock(&cache->lock);
        spin_unlock(&sinfo->lock);
-       return 0;
 }
 
 /*
@@ -7222,6 +7296,7 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr)
        u64 min_free;
        u64 dev_min = 1;
        u64 dev_nr = 0;
+       u64 target;
        int index;
        int full = 0;
        int ret = 0;
@@ -7262,13 +7337,11 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr)
        /*
         * ok we don't have enough space, but maybe we have free space on our
         * devices to allocate new chunks for relocation, so loop through our
-        * alloc devices and guess if we have enough space.  However, if we
-        * were marked as full, then we know there aren't enough chunks, and we
-        * can just return.
+        * alloc devices and guess if we have enough space.  if this block
+        * group is going to be restriped, run checks against the target
+        * profile instead of the current one.
         */
        ret = -1;
-       if (full)
-               goto out;
 
        /*
         * index:
@@ -7278,7 +7351,20 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr)
         *      3: raid0
         *      4: single
         */
-       index = get_block_group_index(block_group);
+       target = get_restripe_target(root->fs_info, block_group->flags);
+       if (target) {
+               index = __get_block_group_index(extended_to_chunk(target));
+       } else {
+               /*
+                * this is just a balance, so if we were marked as full
+                * we know there is no space for a new chunk
+                */
+               if (full)
+                       goto out;
+
+               index = get_block_group_index(block_group);
+       }
+
        if (index == 0) {
                dev_min = 4;
                /* Divide by 2 */
@@ -7572,7 +7658,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
                ret = update_space_info(info, cache->flags, found_key.offset,
                                        btrfs_block_group_used(&cache->item),
                                        &space_info);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
                cache->space_info = space_info;
                spin_lock(&cache->space_info->lock);
                cache->space_info->bytes_readonly += cache->bytes_super;
@@ -7581,7 +7667,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
                __link_block_group(space_info, cache);
 
                ret = btrfs_add_block_group_cache(root->fs_info, cache);
-               BUG_ON(ret);
+               BUG_ON(ret); /* Logic error */
 
                set_avail_alloc_bits(root->fs_info, cache->flags);
                if (btrfs_chunk_readonly(root, cache->key.objectid))
@@ -7663,7 +7749,7 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
 
        ret = update_space_info(root->fs_info, cache->flags, size, bytes_used,
                                &cache->space_info);
-       BUG_ON(ret);
+       BUG_ON(ret); /* -ENOMEM */
        update_global_block_rsv(root->fs_info);
 
        spin_lock(&cache->space_info->lock);
@@ -7673,11 +7759,14 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
        __link_block_group(cache->space_info, cache);
 
        ret = btrfs_add_block_group_cache(root->fs_info, cache);
-       BUG_ON(ret);
+       BUG_ON(ret); /* Logic error */
 
        ret = btrfs_insert_item(trans, extent_root, &cache->key, &cache->item,
                                sizeof(cache->item));
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, extent_root, ret);
+               return ret;
+       }
 
        set_avail_alloc_bits(extent_root->fs_info, type);
 
@@ -7686,11 +7775,8 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
 
 static void clear_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
 {
-       u64 extra_flags = flags & BTRFS_BLOCK_GROUP_PROFILE_MASK;
-
-       /* chunk -> extended profile */
-       if (extra_flags == 0)
-               extra_flags = BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+       u64 extra_flags = chunk_to_extended(flags) &
+                               BTRFS_EXTENDED_PROFILE_MASK;
 
        if (flags & BTRFS_BLOCK_GROUP_DATA)
                fs_info->avail_data_alloc_bits &= ~extra_flags;
@@ -7758,7 +7844,10 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
        inode = lookup_free_space_inode(tree_root, block_group, path);
        if (!IS_ERR(inode)) {
                ret = btrfs_orphan_add(trans, inode);
-               BUG_ON(ret);
+               if (ret) {
+                       btrfs_add_delayed_iput(inode);
+                       goto out;
+               }
                clear_nlink(inode);
                /* One for the block groups ref */
                spin_lock(&block_group->lock);
index 2862454bcdb32fb78b435b1806d5db297a65f774..8d904dd7ea9f6175012d8abdcf810a7d8d200ae7 100644 (file)
@@ -19,6 +19,7 @@
 #include "btrfs_inode.h"
 #include "volumes.h"
 #include "check-integrity.h"
+#include "locking.h"
 
 static struct kmem_cache *extent_state_cache;
 static struct kmem_cache *extent_buffer_cache;
@@ -53,6 +54,13 @@ struct extent_page_data {
        unsigned int sync_io:1;
 };
 
+static noinline void flush_write_bio(void *data);
+static inline struct btrfs_fs_info *
+tree_fs_info(struct extent_io_tree *tree)
+{
+       return btrfs_sb(tree->mapping->host->i_sb);
+}
+
 int __init extent_io_init(void)
 {
        extent_state_cache = kmem_cache_create("extent_state",
@@ -136,6 +144,7 @@ static struct extent_state *alloc_extent_state(gfp_t mask)
 #endif
        atomic_set(&state->refs, 1);
        init_waitqueue_head(&state->wq);
+       trace_alloc_extent_state(state, mask, _RET_IP_);
        return state;
 }
 
@@ -153,6 +162,7 @@ void free_extent_state(struct extent_state *state)
                list_del(&state->leak_list);
                spin_unlock_irqrestore(&leak_lock, flags);
 #endif
+               trace_free_extent_state(state, _RET_IP_);
                kmem_cache_free(extent_state_cache, state);
        }
 }
@@ -439,6 +449,13 @@ alloc_extent_state_atomic(struct extent_state *prealloc)
        return prealloc;
 }
 
+void extent_io_tree_panic(struct extent_io_tree *tree, int err)
+{
+       btrfs_panic(tree_fs_info(tree), err, "Locking error: "
+                   "Extent tree was modified by another "
+                   "thread while locked.");
+}
+
 /*
  * clear some bits on a range in the tree.  This may require splitting
  * or inserting elements in the tree, so the gfp mask is used to
@@ -449,8 +466,7 @@ alloc_extent_state_atomic(struct extent_state *prealloc)
  *
  * the range [start, end] is inclusive.
  *
- * This takes the tree lock, and returns < 0 on error, > 0 if any of the
- * bits were already set, or zero if none of the bits were already set.
+ * This takes the tree lock, and returns 0 on success and < 0 on error.
  */
 int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
                     int bits, int wake, int delete,
@@ -464,7 +480,6 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
        struct rb_node *node;
        u64 last_end;
        int err;
-       int set = 0;
        int clear = 0;
 
        if (delete)
@@ -542,12 +557,14 @@ hit_next:
                prealloc = alloc_extent_state_atomic(prealloc);
                BUG_ON(!prealloc);
                err = split_state(tree, state, prealloc, start);
-               BUG_ON(err == -EEXIST);
+               if (err)
+                       extent_io_tree_panic(tree, err);
+
                prealloc = NULL;
                if (err)
                        goto out;
                if (state->end <= end) {
-                       set |= clear_state_bit(tree, state, &bits, wake);
+                       clear_state_bit(tree, state, &bits, wake);
                        if (last_end == (u64)-1)
                                goto out;
                        start = last_end + 1;
@@ -564,17 +581,19 @@ hit_next:
                prealloc = alloc_extent_state_atomic(prealloc);
                BUG_ON(!prealloc);
                err = split_state(tree, state, prealloc, end + 1);
-               BUG_ON(err == -EEXIST);
+               if (err)
+                       extent_io_tree_panic(tree, err);
+
                if (wake)
                        wake_up(&state->wq);
 
-               set |= clear_state_bit(tree, prealloc, &bits, wake);
+               clear_state_bit(tree, prealloc, &bits, wake);
 
                prealloc = NULL;
                goto out;
        }
 
-       set |= clear_state_bit(tree, state, &bits, wake);
+       clear_state_bit(tree, state, &bits, wake);
 next:
        if (last_end == (u64)-1)
                goto out;
@@ -591,7 +610,7 @@ out:
        if (prealloc)
                free_extent_state(prealloc);
 
-       return set;
+       return 0;
 
 search_again:
        if (start > end)
@@ -602,8 +621,8 @@ search_again:
        goto again;
 }
 
-static int wait_on_state(struct extent_io_tree *tree,
-                        struct extent_state *state)
+static void wait_on_state(struct extent_io_tree *tree,
+                         struct extent_state *state)
                __releases(tree->lock)
                __acquires(tree->lock)
 {
@@ -613,7 +632,6 @@ static int wait_on_state(struct extent_io_tree *tree,
        schedule();
        spin_lock(&tree->lock);
        finish_wait(&state->wq, &wait);
-       return 0;
 }
 
 /*
@@ -621,7 +639,7 @@ static int wait_on_state(struct extent_io_tree *tree,
  * The range [start, end] is inclusive.
  * The tree lock is taken by this function
  */
-int wait_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits)
+void wait_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits)
 {
        struct extent_state *state;
        struct rb_node *node;
@@ -658,7 +676,6 @@ again:
        }
 out:
        spin_unlock(&tree->lock);
-       return 0;
 }
 
 static void set_state_bits(struct extent_io_tree *tree,
@@ -706,9 +723,10 @@ static void uncache_state(struct extent_state **cached_ptr)
  * [start, end] is inclusive This takes the tree lock.
  */
 
-int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
-                  int bits, int exclusive_bits, u64 *failed_start,
-                  struct extent_state **cached_state, gfp_t mask)
+static int __must_check
+__set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
+                int bits, int exclusive_bits, u64 *failed_start,
+                struct extent_state **cached_state, gfp_t mask)
 {
        struct extent_state *state;
        struct extent_state *prealloc = NULL;
@@ -742,8 +760,10 @@ again:
                prealloc = alloc_extent_state_atomic(prealloc);
                BUG_ON(!prealloc);
                err = insert_state(tree, prealloc, start, end, &bits);
+               if (err)
+                       extent_io_tree_panic(tree, err);
+
                prealloc = NULL;
-               BUG_ON(err == -EEXIST);
                goto out;
        }
        state = rb_entry(node, struct extent_state, rb_node);
@@ -809,7 +829,9 @@ hit_next:
                prealloc = alloc_extent_state_atomic(prealloc);
                BUG_ON(!prealloc);
                err = split_state(tree, state, prealloc, start);
-               BUG_ON(err == -EEXIST);
+               if (err)
+                       extent_io_tree_panic(tree, err);
+
                prealloc = NULL;
                if (err)
                        goto out;
@@ -846,12 +868,9 @@ hit_next:
                 */
                err = insert_state(tree, prealloc, start, this_end,
                                   &bits);
-               BUG_ON(err == -EEXIST);
-               if (err) {
-                       free_extent_state(prealloc);
-                       prealloc = NULL;
-                       goto out;
-               }
+               if (err)
+                       extent_io_tree_panic(tree, err);
+
                cache_state(prealloc, cached_state);
                prealloc = NULL;
                start = this_end + 1;
@@ -873,7 +892,8 @@ hit_next:
                prealloc = alloc_extent_state_atomic(prealloc);
                BUG_ON(!prealloc);
                err = split_state(tree, state, prealloc, end + 1);
-               BUG_ON(err == -EEXIST);
+               if (err)
+                       extent_io_tree_panic(tree, err);
 
                set_state_bits(tree, prealloc, &bits);
                cache_state(prealloc, cached_state);
@@ -900,6 +920,15 @@ search_again:
        goto again;
 }
 
+int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits,
+                  u64 *failed_start, struct extent_state **cached_state,
+                  gfp_t mask)
+{
+       return __set_extent_bit(tree, start, end, bits, 0, failed_start,
+                               cached_state, mask);
+}
+
+
 /**
  * convert_extent - convert all bits in a given range from one bit to another
  * @tree:      the io tree to search
@@ -946,7 +975,8 @@ again:
                }
                err = insert_state(tree, prealloc, start, end, &bits);
                prealloc = NULL;
-               BUG_ON(err == -EEXIST);
+               if (err)
+                       extent_io_tree_panic(tree, err);
                goto out;
        }
        state = rb_entry(node, struct extent_state, rb_node);
@@ -1002,7 +1032,8 @@ hit_next:
                        goto out;
                }
                err = split_state(tree, state, prealloc, start);
-               BUG_ON(err == -EEXIST);
+               if (err)
+                       extent_io_tree_panic(tree, err);
                prealloc = NULL;
                if (err)
                        goto out;
@@ -1041,12 +1072,8 @@ hit_next:
                 */
                err = insert_state(tree, prealloc, start, this_end,
                                   &bits);
-               BUG_ON(err == -EEXIST);
-               if (err) {
-                       free_extent_state(prealloc);
-                       prealloc = NULL;
-                       goto out;
-               }
+               if (err)
+                       extent_io_tree_panic(tree, err);
                prealloc = NULL;
                start = this_end + 1;
                goto search_again;
@@ -1065,7 +1092,8 @@ hit_next:
                }
 
                err = split_state(tree, state, prealloc, end + 1);
-               BUG_ON(err == -EEXIST);
+               if (err)
+                       extent_io_tree_panic(tree, err);
 
                set_state_bits(tree, prealloc, &bits);
                clear_state_bit(tree, prealloc, &clear_bits, 0);
@@ -1095,14 +1123,14 @@ search_again:
 int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
                     gfp_t mask)
 {
-       return set_extent_bit(tree, start, end, EXTENT_DIRTY, 0, NULL,
+       return set_extent_bit(tree, start, end, EXTENT_DIRTY, NULL,
                              NULL, mask);
 }
 
 int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
                    int bits, gfp_t mask)
 {
-       return set_extent_bit(tree, start, end, bits, 0, NULL,
+       return set_extent_bit(tree, start, end, bits, NULL,
                              NULL, mask);
 }
 
@@ -1117,7 +1145,7 @@ int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end,
 {
        return set_extent_bit(tree, start, end,
                              EXTENT_DELALLOC | EXTENT_UPTODATE,
-                             0, NULL, cached_state, mask);
+                             NULL, cached_state, mask);
 }
 
 int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
@@ -1131,7 +1159,7 @@ int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
 int set_extent_new(struct extent_io_tree *tree, u64 start, u64 end,
                     gfp_t mask)
 {
-       return set_extent_bit(tree, start, end, EXTENT_NEW, 0, NULL,
+       return set_extent_bit(tree, start, end, EXTENT_NEW, NULL,
                              NULL, mask);
 }
 
@@ -1139,7 +1167,7 @@ int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
                        struct extent_state **cached_state, gfp_t mask)
 {
        return set_extent_bit(tree, start, end, EXTENT_UPTODATE, 0,
-                             NULL, cached_state, mask);
+                             cached_state, mask);
 }
 
 static int clear_extent_uptodate(struct extent_io_tree *tree, u64 start,
@@ -1155,42 +1183,40 @@ static int clear_extent_uptodate(struct extent_io_tree *tree, u64 start,
  * us if waiting is desired.
  */
 int lock_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
-                    int bits, struct extent_state **cached_state, gfp_t mask)
+                    int bits, struct extent_state **cached_state)
 {
        int err;
        u64 failed_start;
        while (1) {
-               err = set_extent_bit(tree, start, end, EXTENT_LOCKED | bits,
-                                    EXTENT_LOCKED, &failed_start,
-                                    cached_state, mask);
-               if (err == -EEXIST && (mask & __GFP_WAIT)) {
+               err = __set_extent_bit(tree, start, end, EXTENT_LOCKED | bits,
+                                      EXTENT_LOCKED, &failed_start,
+                                      cached_state, GFP_NOFS);
+               if (err == -EEXIST) {
                        wait_extent_bit(tree, failed_start, end, EXTENT_LOCKED);
                        start = failed_start;
-               } else {
+               } else
                        break;
-               }
                WARN_ON(start > end);
        }
        return err;
 }
 
-int lock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask)
+int lock_extent(struct extent_io_tree *tree, u64 start, u64 end)
 {
-       return lock_extent_bits(tree, start, end, 0, NULL, mask);
+       return lock_extent_bits(tree, start, end, 0, NULL);
 }
 
-int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end,
-                   gfp_t mask)
+int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end)
 {
        int err;
        u64 failed_start;
 
-       err = set_extent_bit(tree, start, end, EXTENT_LOCKED, EXTENT_LOCKED,
-                            &failed_start, NULL, mask);
+       err = __set_extent_bit(tree, start, end, EXTENT_LOCKED, EXTENT_LOCKED,
+                              &failed_start, NULL, GFP_NOFS);
        if (err == -EEXIST) {
                if (failed_start > start)
                        clear_extent_bit(tree, start, failed_start - 1,
-                                        EXTENT_LOCKED, 1, 0, NULL, mask);
+                                        EXTENT_LOCKED, 1, 0, NULL, GFP_NOFS);
                return 0;
        }
        return 1;
@@ -1203,10 +1229,10 @@ int unlock_extent_cached(struct extent_io_tree *tree, u64 start, u64 end,
                                mask);
 }
 
-int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask)
+int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end)
 {
        return clear_extent_bit(tree, start, end, EXTENT_LOCKED, 1, 0, NULL,
-                               mask);
+                               GFP_NOFS);
 }
 
 /*
@@ -1220,7 +1246,7 @@ static int set_range_writeback(struct extent_io_tree *tree, u64 start, u64 end)
 
        while (index <= end_index) {
                page = find_get_page(tree->mapping, index);
-               BUG_ON(!page);
+               BUG_ON(!page); /* Pages should be in the extent_io_tree */
                set_page_writeback(page);
                page_cache_release(page);
                index++;
@@ -1343,9 +1369,9 @@ out:
        return found;
 }
 
-static noinline int __unlock_for_delalloc(struct inode *inode,
-                                         struct page *locked_page,
-                                         u64 start, u64 end)
+static noinline void __unlock_for_delalloc(struct inode *inode,
+                                          struct page *locked_page,
+                                          u64 start, u64 end)
 {
        int ret;
        struct page *pages[16];
@@ -1355,7 +1381,7 @@ static noinline int __unlock_for_delalloc(struct inode *inode,
        int i;
 
        if (index == locked_page->index && end_index == index)
-               return 0;
+               return;
 
        while (nr_pages > 0) {
                ret = find_get_pages_contig(inode->i_mapping, index,
@@ -1370,7 +1396,6 @@ static noinline int __unlock_for_delalloc(struct inode *inode,
                index += ret;
                cond_resched();
        }
-       return 0;
 }
 
 static noinline int lock_delalloc_pages(struct inode *inode,
@@ -1500,11 +1525,10 @@ again:
                        goto out_failed;
                }
        }
-       BUG_ON(ret);
+       BUG_ON(ret); /* Only valid values are 0 and -EAGAIN */
 
        /* step three, lock the state bits for the whole range */
-       lock_extent_bits(tree, delalloc_start, delalloc_end,
-                        0, &cached_state, GFP_NOFS);
+       lock_extent_bits(tree, delalloc_start, delalloc_end, 0, &cached_state);
 
        /* then test to make sure it is all still delalloc */
        ret = test_range_bit(tree, delalloc_start, delalloc_end,
@@ -1761,39 +1785,34 @@ int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end,
  * helper function to set a given page up to date if all the
  * extents in the tree for that page are up to date
  */
-static int check_page_uptodate(struct extent_io_tree *tree,
-                              struct page *page)
+static void check_page_uptodate(struct extent_io_tree *tree, struct page *page)
 {
        u64 start = (u64)page->index << PAGE_CACHE_SHIFT;
        u64 end = start + PAGE_CACHE_SIZE - 1;
        if (test_range_bit(tree, start, end, EXTENT_UPTODATE, 1, NULL))
                SetPageUptodate(page);
-       return 0;
 }
 
 /*
  * helper function to unlock a page if all the extents in the tree
  * for that page are unlocked
  */
-static int check_page_locked(struct extent_io_tree *tree,
-                            struct page *page)
+static void check_page_locked(struct extent_io_tree *tree, struct page *page)
 {
        u64 start = (u64)page->index << PAGE_CACHE_SHIFT;
        u64 end = start + PAGE_CACHE_SIZE - 1;
        if (!test_range_bit(tree, start, end, EXTENT_LOCKED, 0, NULL))
                unlock_page(page);
-       return 0;
 }
 
 /*
  * helper function to end page writeback if all the extents
  * in the tree for that page are done with writeback
  */
-static int check_page_writeback(struct extent_io_tree *tree,
-                            struct page *page)
+static void check_page_writeback(struct extent_io_tree *tree,
+                                struct page *page)
 {
        end_page_writeback(page);
-       return 0;
 }
 
 /*
@@ -1912,6 +1931,26 @@ int repair_io_failure(struct btrfs_mapping_tree *map_tree, u64 start,
        return 0;
 }
 
+int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
+                        int mirror_num)
+{
+       struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
+       u64 start = eb->start;
+       unsigned long i, num_pages = num_extent_pages(eb->start, eb->len);
+       int ret;
+
+       for (i = 0; i < num_pages; i++) {
+               struct page *p = extent_buffer_page(eb, i);
+               ret = repair_io_failure(map_tree, start, PAGE_CACHE_SIZE,
+                                       start, p, mirror_num);
+               if (ret)
+                       break;
+               start += PAGE_CACHE_SIZE;
+       }
+
+       return ret;
+}
+
 /*
  * each time an IO finishes, we do a fast check in the IO failure tree
  * to see if we need to process or clean up an io_failure_record
@@ -2258,6 +2297,7 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
        u64 start;
        u64 end;
        int whole_page;
+       int failed_mirror;
        int ret;
 
        if (err)
@@ -2304,9 +2344,16 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
                        else
                                clean_io_failure(start, page);
                }
-               if (!uptodate) {
-                       int failed_mirror;
+
+               if (!uptodate)
                        failed_mirror = (int)(unsigned long)bio->bi_bdev;
+
+               if (!uptodate && tree->ops && tree->ops->readpage_io_failed_hook) {
+                       ret = tree->ops->readpage_io_failed_hook(page, failed_mirror);
+                       if (!ret && !err &&
+                           test_bit(BIO_UPTODATE, &bio->bi_flags))
+                               uptodate = 1;
+               } else if (!uptodate) {
                        /*
                         * The generic bio_readpage_error handles errors the
                         * following way: If possible, new read requests are
@@ -2320,7 +2367,6 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
                        ret = bio_readpage_error(bio, page, start, end,
                                                        failed_mirror, NULL);
                        if (ret == 0) {
-error_handled:
                                uptodate =
                                        test_bit(BIO_UPTODATE, &bio->bi_flags);
                                if (err)
@@ -2328,16 +2374,9 @@ error_handled:
                                uncache_state(&cached);
                                continue;
                        }
-                       if (tree->ops && tree->ops->readpage_io_failed_hook) {
-                               ret = tree->ops->readpage_io_failed_hook(
-                                                       bio, page, start, end,
-                                                       failed_mirror, state);
-                               if (ret == 0)
-                                       goto error_handled;
-                       }
                }
 
-               if (uptodate) {
+               if (uptodate && tree->track_uptodate) {
                        set_extent_uptodate(tree, start, end, &cached,
                                            GFP_ATOMIC);
                }
@@ -2386,8 +2425,12 @@ btrfs_bio_alloc(struct block_device *bdev, u64 first_sector, int nr_vecs,
        return bio;
 }
 
-static int submit_one_bio(int rw, struct bio *bio, int mirror_num,
-                         unsigned long bio_flags)
+/*
+ * Since writes are async, they will only return -ENOMEM.
+ * Reads can return the full range of I/O error conditions.
+ */
+static int __must_check submit_one_bio(int rw, struct bio *bio,
+                                      int mirror_num, unsigned long bio_flags)
 {
        int ret = 0;
        struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
@@ -2413,6 +2456,19 @@ static int submit_one_bio(int rw, struct bio *bio, int mirror_num,
        return ret;
 }
 
+static int merge_bio(struct extent_io_tree *tree, struct page *page,
+                    unsigned long offset, size_t size, struct bio *bio,
+                    unsigned long bio_flags)
+{
+       int ret = 0;
+       if (tree->ops && tree->ops->merge_bio_hook)
+               ret = tree->ops->merge_bio_hook(page, offset, size, bio,
+                                               bio_flags);
+       BUG_ON(ret < 0);
+       return ret;
+
+}
+
 static int submit_extent_page(int rw, struct extent_io_tree *tree,
                              struct page *page, sector_t sector,
                              size_t size, unsigned long offset,
@@ -2441,12 +2497,12 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree,
                                sector;
 
                if (prev_bio_flags != bio_flags || !contig ||
-                   (tree->ops && tree->ops->merge_bio_hook &&
-                    tree->ops->merge_bio_hook(page, offset, page_size, bio,
-                                              bio_flags)) ||
+                   merge_bio(tree, page, offset, page_size, bio, bio_flags) ||
                    bio_add_page(bio, page, page_size, offset) < page_size) {
                        ret = submit_one_bio(rw, bio, mirror_num,
                                             prev_bio_flags);
+                       if (ret < 0)
+                               return ret;
                        bio = NULL;
                } else {
                        return 0;
@@ -2473,25 +2529,31 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree,
        return ret;
 }
 
-void set_page_extent_mapped(struct page *page)
+void attach_extent_buffer_page(struct extent_buffer *eb, struct page *page)
 {
        if (!PagePrivate(page)) {
                SetPagePrivate(page);
                page_cache_get(page);
-               set_page_private(page, EXTENT_PAGE_PRIVATE);
+               set_page_private(page, (unsigned long)eb);
+       } else {
+               WARN_ON(page->private != (unsigned long)eb);
        }
 }
 
-static void set_page_extent_head(struct page *page, unsigned long len)
+void set_page_extent_mapped(struct page *page)
 {
-       WARN_ON(!PagePrivate(page));
-       set_page_private(page, EXTENT_PAGE_PRIVATE_FIRST_PAGE | len << 2);
+       if (!PagePrivate(page)) {
+               SetPagePrivate(page);
+               page_cache_get(page);
+               set_page_private(page, EXTENT_PAGE_PRIVATE);
+       }
 }
 
 /*
  * basic readpage implementation.  Locked extent state structs are inserted
  * into the tree that are removed when the IO is done (by the end_io
  * handlers)
+ * XXX JDM: This needs looking at to ensure proper page locking
  */
 static int __extent_read_full_page(struct extent_io_tree *tree,
                                   struct page *page,
@@ -2531,11 +2593,11 @@ static int __extent_read_full_page(struct extent_io_tree *tree,
 
        end = page_end;
        while (1) {
-               lock_extent(tree, start, end, GFP_NOFS);
+               lock_extent(tree, start, end);
                ordered = btrfs_lookup_ordered_extent(inode, start);
                if (!ordered)
                        break;
-               unlock_extent(tree, start, end, GFP_NOFS);
+               unlock_extent(tree, start, end);
                btrfs_start_ordered_extent(inode, ordered, 1);
                btrfs_put_ordered_extent(ordered);
        }
@@ -2572,7 +2634,7 @@ static int __extent_read_full_page(struct extent_io_tree *tree,
                                end - cur + 1, 0);
                if (IS_ERR_OR_NULL(em)) {
                        SetPageError(page);
-                       unlock_extent(tree, cur, end, GFP_NOFS);
+                       unlock_extent(tree, cur, end);
                        break;
                }
                extent_offset = cur - em->start;
@@ -2624,7 +2686,7 @@ static int __extent_read_full_page(struct extent_io_tree *tree,
                if (test_range_bit(tree, cur, cur_end,
                                   EXTENT_UPTODATE, 1, NULL)) {
                        check_page_uptodate(tree, page);
-                       unlock_extent(tree, cur, cur + iosize - 1, GFP_NOFS);
+                       unlock_extent(tree, cur, cur + iosize - 1);
                        cur = cur + iosize;
                        pg_offset += iosize;
                        continue;
@@ -2634,7 +2696,7 @@ static int __extent_read_full_page(struct extent_io_tree *tree,
                 */
                if (block_start == EXTENT_MAP_INLINE) {
                        SetPageError(page);
-                       unlock_extent(tree, cur, cur + iosize - 1, GFP_NOFS);
+                       unlock_extent(tree, cur, cur + iosize - 1);
                        cur = cur + iosize;
                        pg_offset += iosize;
                        continue;
@@ -2654,6 +2716,7 @@ static int __extent_read_full_page(struct extent_io_tree *tree,
                                         end_bio_extent_readpage, mirror_num,
                                         *bio_flags,
                                         this_bio_flag);
+                       BUG_ON(ret == -ENOMEM);
                        nr++;
                        *bio_flags = this_bio_flag;
                }
@@ -2795,7 +2858,11 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
                                                       delalloc_end,
                                                       &page_started,
                                                       &nr_written);
-                       BUG_ON(ret);
+                       /* File system has been set read-only */
+                       if (ret) {
+                               SetPageError(page);
+                               goto done;
+                       }
                        /*
                         * delalloc_end is already one less than the total
                         * length, so we don't subtract one from
@@ -2968,6 +3035,275 @@ done_unlocked:
        return 0;
 }
 
+static int eb_wait(void *word)
+{
+       io_schedule();
+       return 0;
+}
+
+static void wait_on_extent_buffer_writeback(struct extent_buffer *eb)
+{
+       wait_on_bit(&eb->bflags, EXTENT_BUFFER_WRITEBACK, eb_wait,
+                   TASK_UNINTERRUPTIBLE);
+}
+
+static int lock_extent_buffer_for_io(struct extent_buffer *eb,
+                                    struct btrfs_fs_info *fs_info,
+                                    struct extent_page_data *epd)
+{
+       unsigned long i, num_pages;
+       int flush = 0;
+       int ret = 0;
+
+       if (!btrfs_try_tree_write_lock(eb)) {
+               flush = 1;
+               flush_write_bio(epd);
+               btrfs_tree_lock(eb);
+       }
+
+       if (test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags)) {
+               btrfs_tree_unlock(eb);
+               if (!epd->sync_io)
+                       return 0;
+               if (!flush) {
+                       flush_write_bio(epd);
+                       flush = 1;
+               }
+               while (1) {
+                       wait_on_extent_buffer_writeback(eb);
+                       btrfs_tree_lock(eb);
+                       if (!test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags))
+                               break;
+                       btrfs_tree_unlock(eb);
+               }
+       }
+
+       if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
+               set_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags);
+               btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
+               spin_lock(&fs_info->delalloc_lock);
+               if (fs_info->dirty_metadata_bytes >= eb->len)
+                       fs_info->dirty_metadata_bytes -= eb->len;
+               else
+                       WARN_ON(1);
+               spin_unlock(&fs_info->delalloc_lock);
+               ret = 1;
+       }
+
+       btrfs_tree_unlock(eb);
+
+       if (!ret)
+               return ret;
+
+       num_pages = num_extent_pages(eb->start, eb->len);
+       for (i = 0; i < num_pages; i++) {
+               struct page *p = extent_buffer_page(eb, i);
+
+               if (!trylock_page(p)) {
+                       if (!flush) {
+                               flush_write_bio(epd);
+                               flush = 1;
+                       }
+                       lock_page(p);
+               }
+       }
+
+       return ret;
+}
+
+static void end_extent_buffer_writeback(struct extent_buffer *eb)
+{
+       clear_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags);
+       smp_mb__after_clear_bit();
+       wake_up_bit(&eb->bflags, EXTENT_BUFFER_WRITEBACK);
+}
+
+static void end_bio_extent_buffer_writepage(struct bio *bio, int err)
+{
+       int uptodate = err == 0;
+       struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
+       struct extent_buffer *eb;
+       int done;
+
+       do {
+               struct page *page = bvec->bv_page;
+
+               bvec--;
+               eb = (struct extent_buffer *)page->private;
+               BUG_ON(!eb);
+               done = atomic_dec_and_test(&eb->io_pages);
+
+               if (!uptodate || test_bit(EXTENT_BUFFER_IOERR, &eb->bflags)) {
+                       set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
+                       ClearPageUptodate(page);
+                       SetPageError(page);
+               }
+
+               end_page_writeback(page);
+
+               if (!done)
+                       continue;
+
+               end_extent_buffer_writeback(eb);
+       } while (bvec >= bio->bi_io_vec);
+
+       bio_put(bio);
+
+}
+
+static int write_one_eb(struct extent_buffer *eb,
+                       struct btrfs_fs_info *fs_info,
+                       struct writeback_control *wbc,
+                       struct extent_page_data *epd)
+{
+       struct block_device *bdev = fs_info->fs_devices->latest_bdev;
+       u64 offset = eb->start;
+       unsigned long i, num_pages;
+       int rw = (epd->sync_io ? WRITE_SYNC : WRITE);
+       int ret;
+
+       clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
+       num_pages = num_extent_pages(eb->start, eb->len);
+       atomic_set(&eb->io_pages, num_pages);
+       for (i = 0; i < num_pages; i++) {
+               struct page *p = extent_buffer_page(eb, i);
+
+               clear_page_dirty_for_io(p);
+               set_page_writeback(p);
+               ret = submit_extent_page(rw, eb->tree, p, offset >> 9,
+                                        PAGE_CACHE_SIZE, 0, bdev, &epd->bio,
+                                        -1, end_bio_extent_buffer_writepage,
+                                        0, 0, 0);
+               if (ret) {
+                       set_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
+                       SetPageError(p);
+                       if (atomic_sub_and_test(num_pages - i, &eb->io_pages))
+                               end_extent_buffer_writeback(eb);
+                       ret = -EIO;
+                       break;
+               }
+               offset += PAGE_CACHE_SIZE;
+               update_nr_written(p, wbc, 1);
+               unlock_page(p);
+       }
+
+       if (unlikely(ret)) {
+               for (; i < num_pages; i++) {
+                       struct page *p = extent_buffer_page(eb, i);
+                       unlock_page(p);
+               }
+       }
+
+       return ret;
+}
+
+int btree_write_cache_pages(struct address_space *mapping,
+                                  struct writeback_control *wbc)
+{
+       struct extent_io_tree *tree = &BTRFS_I(mapping->host)->io_tree;
+       struct btrfs_fs_info *fs_info = BTRFS_I(mapping->host)->root->fs_info;
+       struct extent_buffer *eb, *prev_eb = NULL;
+       struct extent_page_data epd = {
+               .bio = NULL,
+               .tree = tree,
+               .extent_locked = 0,
+               .sync_io = wbc->sync_mode == WB_SYNC_ALL,
+       };
+       int ret = 0;
+       int done = 0;
+       int nr_to_write_done = 0;
+       struct pagevec pvec;
+       int nr_pages;
+       pgoff_t index;
+       pgoff_t end;            /* Inclusive */
+       int scanned = 0;
+       int tag;
+
+       pagevec_init(&pvec, 0);
+       if (wbc->range_cyclic) {
+               index = mapping->writeback_index; /* Start from prev offset */
+               end = -1;
+       } else {
+               index = wbc->range_start >> PAGE_CACHE_SHIFT;
+               end = wbc->range_end >> PAGE_CACHE_SHIFT;
+               scanned = 1;
+       }
+       if (wbc->sync_mode == WB_SYNC_ALL)
+               tag = PAGECACHE_TAG_TOWRITE;
+       else
+               tag = PAGECACHE_TAG_DIRTY;
+retry:
+       if (wbc->sync_mode == WB_SYNC_ALL)
+               tag_pages_for_writeback(mapping, index, end);
+       while (!done && !nr_to_write_done && (index <= end) &&
+              (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, tag,
+                       min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1))) {
+               unsigned i;
+
+               scanned = 1;
+               for (i = 0; i < nr_pages; i++) {
+                       struct page *page = pvec.pages[i];
+
+                       if (!PagePrivate(page))
+                               continue;
+
+                       if (!wbc->range_cyclic && page->index > end) {
+                               done = 1;
+                               break;
+                       }
+
+                       eb = (struct extent_buffer *)page->private;
+                       if (!eb) {
+                               WARN_ON(1);
+                               continue;
+                       }
+
+                       if (eb == prev_eb)
+                               continue;
+
+                       if (!atomic_inc_not_zero(&eb->refs)) {
+                               WARN_ON(1);
+                               continue;
+                       }
+
+                       prev_eb = eb;
+                       ret = lock_extent_buffer_for_io(eb, fs_info, &epd);
+                       if (!ret) {
+                               free_extent_buffer(eb);
+                               continue;
+                       }
+
+                       ret = write_one_eb(eb, fs_info, wbc, &epd);
+                       if (ret) {
+                               done = 1;
+                               free_extent_buffer(eb);
+                               break;
+                       }
+                       free_extent_buffer(eb);
+
+                       /*
+                        * the filesystem may choose to bump up nr_to_write.
+                        * We have to make sure to honor the new nr_to_write
+                        * at any time
+                        */
+                       nr_to_write_done = wbc->nr_to_write <= 0;
+               }
+               pagevec_release(&pvec);
+               cond_resched();
+       }
+       if (!scanned && !done) {
+               /*
+                * We hit the last page and there is more work to be done: wrap
+                * back to the start of the file
+                */
+               scanned = 1;
+               index = 0;
+               goto retry;
+       }
+       flush_write_bio(&epd);
+       return ret;
+}
+
 /**
  * write_cache_pages - walk the list of dirty pages of the given address space and write all of them.
  * @mapping: address space structure to write
@@ -3099,10 +3435,14 @@ retry:
 static void flush_epd_write_bio(struct extent_page_data *epd)
 {
        if (epd->bio) {
+               int rw = WRITE;
+               int ret;
+
                if (epd->sync_io)
-                       submit_one_bio(WRITE_SYNC, epd->bio, 0, 0);
-               else
-                       submit_one_bio(WRITE, epd->bio, 0, 0);
+                       rw = WRITE_SYNC;
+
+               ret = submit_one_bio(rw, epd->bio, 0, 0);
+               BUG_ON(ret < 0); /* -ENOMEM */
                epd->bio = NULL;
        }
 }
@@ -3219,7 +3559,7 @@ int extent_readpages(struct extent_io_tree *tree,
        }
        BUG_ON(!list_empty(pages));
        if (bio)
-               submit_one_bio(READ, bio, 0, bio_flags);
+               return submit_one_bio(READ, bio, 0, bio_flags);
        return 0;
 }
 
@@ -3240,7 +3580,7 @@ int extent_invalidatepage(struct extent_io_tree *tree,
        if (start > end)
                return 0;
 
-       lock_extent_bits(tree, start, end, 0, &cached_state, GFP_NOFS);
+       lock_extent_bits(tree, start, end, 0, &cached_state);
        wait_on_page_writeback(page);
        clear_extent_bit(tree, start, end,
                         EXTENT_LOCKED | EXTENT_DIRTY | EXTENT_DELALLOC |
@@ -3454,7 +3794,7 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
        }
 
        lock_extent_bits(&BTRFS_I(inode)->io_tree, start, start + len, 0,
-                        &cached_state, GFP_NOFS);
+                        &cached_state);
 
        em = get_extent_skip_holes(inode, start, last_for_get_extent,
                                   get_extent);
@@ -3548,26 +3888,7 @@ out:
 inline struct page *extent_buffer_page(struct extent_buffer *eb,
                                              unsigned long i)
 {
-       struct page *p;
-       struct address_space *mapping;
-
-       if (i == 0)
-               return eb->first_page;
-       i += eb->start >> PAGE_CACHE_SHIFT;
-       mapping = eb->first_page->mapping;
-       if (!mapping)
-               return NULL;
-
-       /*
-        * extent_buffer_page is only called after pinning the page
-        * by increasing the reference count.  So we know the page must
-        * be in the radix tree.
-        */
-       rcu_read_lock();
-       p = radix_tree_lookup(&mapping->page_tree, i);
-       rcu_read_unlock();
-
-       return p;
+       return eb->pages[i];
 }
 
 inline unsigned long num_extent_pages(u64 start, u64 len)
@@ -3576,6 +3897,19 @@ inline unsigned long num_extent_pages(u64 start, u64 len)
                (start >> PAGE_CACHE_SHIFT);
 }
 
+static void __free_extent_buffer(struct extent_buffer *eb)
+{
+#if LEAK_DEBUG
+       unsigned long flags;
+       spin_lock_irqsave(&leak_lock, flags);
+       list_del(&eb->leak_list);
+       spin_unlock_irqrestore(&leak_lock, flags);
+#endif
+       if (eb->pages && eb->pages != eb->inline_pages)
+               kfree(eb->pages);
+       kmem_cache_free(extent_buffer_cache, eb);
+}
+
 static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
                                                   u64 start,
                                                   unsigned long len,
@@ -3591,6 +3925,7 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
                return NULL;
        eb->start = start;
        eb->len = len;
+       eb->tree = tree;
        rwlock_init(&eb->lock);
        atomic_set(&eb->write_locks, 0);
        atomic_set(&eb->read_locks, 0);
@@ -3607,20 +3942,32 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
        list_add(&eb->leak_list, &buffers);
        spin_unlock_irqrestore(&leak_lock, flags);
 #endif
+       spin_lock_init(&eb->refs_lock);
        atomic_set(&eb->refs, 1);
+       atomic_set(&eb->io_pages, 0);
+
+       if (len > MAX_INLINE_EXTENT_BUFFER_SIZE) {
+               struct page **pages;
+               int num_pages = (len + PAGE_CACHE_SIZE - 1) >>
+                       PAGE_CACHE_SHIFT;
+               pages = kzalloc(num_pages, mask);
+               if (!pages) {
+                       __free_extent_buffer(eb);
+                       return NULL;
+               }
+               eb->pages = pages;
+       } else {
+               eb->pages = eb->inline_pages;
+       }
 
        return eb;
 }
 
-static void __free_extent_buffer(struct extent_buffer *eb)
+static int extent_buffer_under_io(struct extent_buffer *eb)
 {
-#if LEAK_DEBUG
-       unsigned long flags;
-       spin_lock_irqsave(&leak_lock, flags);
-       list_del(&eb->leak_list);
-       spin_unlock_irqrestore(&leak_lock, flags);
-#endif
-       kmem_cache_free(extent_buffer_cache, eb);
+       return (atomic_read(&eb->io_pages) ||
+               test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) ||
+               test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
 }
 
 /*
@@ -3632,8 +3979,7 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
        unsigned long index;
        struct page *page;
 
-       if (!eb->first_page)
-               return;
+       BUG_ON(extent_buffer_under_io(eb));
 
        index = num_extent_pages(eb->start, eb->len);
        if (start_idx >= index)
@@ -3642,8 +3988,34 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
        do {
                index--;
                page = extent_buffer_page(eb, index);
-               if (page)
+               if (page) {
+                       spin_lock(&page->mapping->private_lock);
+                       /*
+                        * We do this since we'll remove the pages after we've
+                        * removed the eb from the radix tree, so we could race
+                        * and have this page now attached to the new eb.  So
+                        * only clear page_private if it's still connected to
+                        * this eb.
+                        */
+                       if (PagePrivate(page) &&
+                           page->private == (unsigned long)eb) {
+                               BUG_ON(test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
+                               BUG_ON(PageDirty(page));
+                               BUG_ON(PageWriteback(page));
+                               /*
+                                * We need to make sure we haven't be attached
+                                * to a new eb.
+                                */
+                               ClearPagePrivate(page);
+                               set_page_private(page, 0);
+                               /* One for the page private */
+                               page_cache_release(page);
+                       }
+                       spin_unlock(&page->mapping->private_lock);
+
+                       /* One for when we alloced the page */
                        page_cache_release(page);
+               }
        } while (index != start_idx);
 }
 
@@ -3656,9 +4028,50 @@ static inline void btrfs_release_extent_buffer(struct extent_buffer *eb)
        __free_extent_buffer(eb);
 }
 
+static void check_buffer_tree_ref(struct extent_buffer *eb)
+{
+       /* the ref bit is tricky.  We have to make sure it is set
+        * if we have the buffer dirty.   Otherwise the
+        * code to free a buffer can end up dropping a dirty
+        * page
+        *
+        * Once the ref bit is set, it won't go away while the
+        * buffer is dirty or in writeback, and it also won't
+        * go away while we have the reference count on the
+        * eb bumped.
+        *
+        * We can't just set the ref bit without bumping the
+        * ref on the eb because free_extent_buffer might
+        * see the ref bit and try to clear it.  If this happens
+        * free_extent_buffer might end up dropping our original
+        * ref by mistake and freeing the page before we are able
+        * to add one more ref.
+        *
+        * So bump the ref count first, then set the bit.  If someone
+        * beat us to it, drop the ref we added.
+        */
+       if (!test_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags)) {
+               atomic_inc(&eb->refs);
+               if (test_and_set_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags))
+                       atomic_dec(&eb->refs);
+       }
+}
+
+static void mark_extent_buffer_accessed(struct extent_buffer *eb)
+{
+       unsigned long num_pages, i;
+
+       check_buffer_tree_ref(eb);
+
+       num_pages = num_extent_pages(eb->start, eb->len);
+       for (i = 0; i < num_pages; i++) {
+               struct page *p = extent_buffer_page(eb, i);
+               mark_page_accessed(p);
+       }
+}
+
 struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
-                                         u64 start, unsigned long len,
-                                         struct page *page0)
+                                         u64 start, unsigned long len)
 {
        unsigned long num_pages = num_extent_pages(start, len);
        unsigned long i;
@@ -3674,7 +4087,7 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
        eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
        if (eb && atomic_inc_not_zero(&eb->refs)) {
                rcu_read_unlock();
-               mark_page_accessed(eb->first_page);
+               mark_extent_buffer_accessed(eb);
                return eb;
        }
        rcu_read_unlock();
@@ -3683,32 +4096,43 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
        if (!eb)
                return NULL;
 
-       if (page0) {
-               eb->first_page = page0;
-               i = 1;
-               index++;
-               page_cache_get(page0);
-               mark_page_accessed(page0);
-               set_page_extent_mapped(page0);
-               set_page_extent_head(page0, len);
-               uptodate = PageUptodate(page0);
-       } else {
-               i = 0;
-       }
-       for (; i < num_pages; i++, index++) {
+       for (i = 0; i < num_pages; i++, index++) {
                p = find_or_create_page(mapping, index, GFP_NOFS);
                if (!p) {
                        WARN_ON(1);
                        goto free_eb;
                }
-               set_page_extent_mapped(p);
-               mark_page_accessed(p);
-               if (i == 0) {
-                       eb->first_page = p;
-                       set_page_extent_head(p, len);
-               } else {
-                       set_page_private(p, EXTENT_PAGE_PRIVATE);
+
+               spin_lock(&mapping->private_lock);
+               if (PagePrivate(p)) {
+                       /*
+                        * We could have already allocated an eb for this page
+                        * and attached one so lets see if we can get a ref on
+                        * the existing eb, and if we can we know it's good and
+                        * we can just return that one, else we know we can just
+                        * overwrite page->private.
+                        */
+                       exists = (struct extent_buffer *)p->private;
+                       if (atomic_inc_not_zero(&exists->refs)) {
+                               spin_unlock(&mapping->private_lock);
+                               unlock_page(p);
+                               mark_extent_buffer_accessed(exists);
+                               goto free_eb;
+                       }
+
+                       /*
+                        * Do this so attach doesn't complain and we need to
+                        * drop the ref the old guy had.
+                        */
+                       ClearPagePrivate(p);
+                       WARN_ON(PageDirty(p));
+                       page_cache_release(p);
                }
+               attach_extent_buffer_page(eb, p);
+               spin_unlock(&mapping->private_lock);
+               WARN_ON(PageDirty(p));
+               mark_page_accessed(p);
+               eb->pages[i] = p;
                if (!PageUptodate(p))
                        uptodate = 0;
 
@@ -3716,12 +4140,10 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
                 * see below about how we avoid a nasty race with release page
                 * and why we unlock later
                 */
-               if (i != 0)
-                       unlock_page(p);
        }
        if (uptodate)
                set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
-
+again:
        ret = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM);
        if (ret)
                goto free_eb;
@@ -3731,14 +4153,21 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
        if (ret == -EEXIST) {
                exists = radix_tree_lookup(&tree->buffer,
                                                start >> PAGE_CACHE_SHIFT);
-               /* add one reference for the caller */
-               atomic_inc(&exists->refs);
+               if (!atomic_inc_not_zero(&exists->refs)) {
+                       spin_unlock(&tree->buffer_lock);
+                       radix_tree_preload_end();
+                       exists = NULL;
+                       goto again;
+               }
                spin_unlock(&tree->buffer_lock);
                radix_tree_preload_end();
+               mark_extent_buffer_accessed(exists);
                goto free_eb;
        }
        /* add one reference for the tree */
-       atomic_inc(&eb->refs);
+       spin_lock(&eb->refs_lock);
+       check_buffer_tree_ref(eb);
+       spin_unlock(&eb->refs_lock);
        spin_unlock(&tree->buffer_lock);
        radix_tree_preload_end();
 
@@ -3751,15 +4180,20 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
         * after the extent buffer is in the radix tree so
         * it doesn't get lost
         */
-       set_page_extent_mapped(eb->first_page);
-       set_page_extent_head(eb->first_page, eb->len);
-       if (!page0)
-               unlock_page(eb->first_page);
+       SetPageChecked(eb->pages[0]);
+       for (i = 1; i < num_pages; i++) {
+               p = extent_buffer_page(eb, i);
+               ClearPageChecked(p);
+               unlock_page(p);
+       }
+       unlock_page(eb->pages[0]);
        return eb;
 
 free_eb:
-       if (eb->first_page && !page0)
-               unlock_page(eb->first_page);
+       for (i = 0; i < num_pages; i++) {
+               if (eb->pages[i])
+                       unlock_page(eb->pages[i]);
+       }
 
        if (!atomic_dec_and_test(&eb->refs))
                return exists;
@@ -3776,7 +4210,7 @@ struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
        eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
        if (eb && atomic_inc_not_zero(&eb->refs)) {
                rcu_read_unlock();
-               mark_page_accessed(eb->first_page);
+               mark_extent_buffer_accessed(eb);
                return eb;
        }
        rcu_read_unlock();
@@ -3784,19 +4218,71 @@ struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
        return NULL;
 }
 
+static inline void btrfs_release_extent_buffer_rcu(struct rcu_head *head)
+{
+       struct extent_buffer *eb =
+                       container_of(head, struct extent_buffer, rcu_head);
+
+       __free_extent_buffer(eb);
+}
+
+/* Expects to have eb->eb_lock already held */
+static void release_extent_buffer(struct extent_buffer *eb, gfp_t mask)
+{
+       WARN_ON(atomic_read(&eb->refs) == 0);
+       if (atomic_dec_and_test(&eb->refs)) {
+               struct extent_io_tree *tree = eb->tree;
+
+               spin_unlock(&eb->refs_lock);
+
+               spin_lock(&tree->buffer_lock);
+               radix_tree_delete(&tree->buffer,
+                                 eb->start >> PAGE_CACHE_SHIFT);
+               spin_unlock(&tree->buffer_lock);
+
+               /* Should be safe to release our pages at this point */
+               btrfs_release_extent_buffer_page(eb, 0);
+
+               call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu);
+               return;
+       }
+       spin_unlock(&eb->refs_lock);
+}
+
 void free_extent_buffer(struct extent_buffer *eb)
 {
        if (!eb)
                return;
 
-       if (!atomic_dec_and_test(&eb->refs))
+       spin_lock(&eb->refs_lock);
+       if (atomic_read(&eb->refs) == 2 &&
+           test_bit(EXTENT_BUFFER_STALE, &eb->bflags) &&
+           !extent_buffer_under_io(eb) &&
+           test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags))
+               atomic_dec(&eb->refs);
+
+       /*
+        * I know this is terrible, but it's temporary until we stop tracking
+        * the uptodate bits and such for the extent buffers.
+        */
+       release_extent_buffer(eb, GFP_ATOMIC);
+}
+
+void free_extent_buffer_stale(struct extent_buffer *eb)
+{
+       if (!eb)
                return;
 
-       WARN_ON(1);
+       spin_lock(&eb->refs_lock);
+       set_bit(EXTENT_BUFFER_STALE, &eb->bflags);
+
+       if (atomic_read(&eb->refs) == 2 && !extent_buffer_under_io(eb) &&
+           test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags))
+               atomic_dec(&eb->refs);
+       release_extent_buffer(eb, GFP_NOFS);
 }
 
-int clear_extent_buffer_dirty(struct extent_io_tree *tree,
-                             struct extent_buffer *eb)
+void clear_extent_buffer_dirty(struct extent_buffer *eb)
 {
        unsigned long i;
        unsigned long num_pages;
@@ -3812,10 +4298,6 @@ int clear_extent_buffer_dirty(struct extent_io_tree *tree,
                lock_page(page);
                WARN_ON(!PagePrivate(page));
 
-               set_page_extent_mapped(page);
-               if (i == 0)
-                       set_page_extent_head(page, eb->len);
-
                clear_page_dirty_for_io(page);
                spin_lock_irq(&page->mapping->tree_lock);
                if (!PageDirty(page)) {
@@ -3827,24 +4309,29 @@ int clear_extent_buffer_dirty(struct extent_io_tree *tree,
                ClearPageError(page);
                unlock_page(page);
        }
-       return 0;
+       WARN_ON(atomic_read(&eb->refs) == 0);
 }
 
-int set_extent_buffer_dirty(struct extent_io_tree *tree,
-                            struct extent_buffer *eb)
+int set_extent_buffer_dirty(struct extent_buffer *eb)
 {
        unsigned long i;
        unsigned long num_pages;
        int was_dirty = 0;
 
+       check_buffer_tree_ref(eb);
+
        was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags);
+
        num_pages = num_extent_pages(eb->start, eb->len);
+       WARN_ON(atomic_read(&eb->refs) == 0);
+       WARN_ON(!test_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags));
+
        for (i = 0; i < num_pages; i++)
-               __set_page_dirty_nobuffers(extent_buffer_page(eb, i));
+               set_page_dirty(extent_buffer_page(eb, i));
        return was_dirty;
 }
 
-static int __eb_straddles_pages(u64 start, u64 len)
+static int range_straddles_pages(u64 start, u64 len)
 {
        if (len < PAGE_CACHE_SIZE)
                return 1;
@@ -3855,25 +4342,14 @@ static int __eb_straddles_pages(u64 start, u64 len)
        return 0;
 }
 
-static int eb_straddles_pages(struct extent_buffer *eb)
-{
-       return __eb_straddles_pages(eb->start, eb->len);
-}
-
-int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
-                               struct extent_buffer *eb,
-                               struct extent_state **cached_state)
+int clear_extent_buffer_uptodate(struct extent_buffer *eb)
 {
        unsigned long i;
        struct page *page;
        unsigned long num_pages;
 
-       num_pages = num_extent_pages(eb->start, eb->len);
        clear_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
-
-       clear_extent_uptodate(tree, eb->start, eb->start + eb->len - 1,
-                             cached_state, GFP_NOFS);
-
+       num_pages = num_extent_pages(eb->start, eb->len);
        for (i = 0; i < num_pages; i++) {
                page = extent_buffer_page(eb, i);
                if (page)
@@ -3882,27 +4358,16 @@ int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
        return 0;
 }
 
-int set_extent_buffer_uptodate(struct extent_io_tree *tree,
-                               struct extent_buffer *eb)
+int set_extent_buffer_uptodate(struct extent_buffer *eb)
 {
        unsigned long i;
        struct page *page;
        unsigned long num_pages;
 
+       set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
        num_pages = num_extent_pages(eb->start, eb->len);
-
-       if (eb_straddles_pages(eb)) {
-               set_extent_uptodate(tree, eb->start, eb->start + eb->len - 1,
-                                   NULL, GFP_NOFS);
-       }
        for (i = 0; i < num_pages; i++) {
                page = extent_buffer_page(eb, i);
-               if ((i == 0 && (eb->start & (PAGE_CACHE_SIZE - 1))) ||
-                   ((i == num_pages - 1) &&
-                    ((eb->start + eb->len) & (PAGE_CACHE_SIZE - 1)))) {
-                       check_page_uptodate(tree, page);
-                       continue;
-               }
                SetPageUptodate(page);
        }
        return 0;
@@ -3917,7 +4382,7 @@ int extent_range_uptodate(struct extent_io_tree *tree,
        int uptodate;
        unsigned long index;
 
-       if (__eb_straddles_pages(start, end - start + 1)) {
+       if (range_straddles_pages(start, end - start + 1)) {
                ret = test_range_bit(tree, start, end,
                                     EXTENT_UPTODATE, 1, NULL);
                if (ret)
@@ -3939,35 +4404,9 @@ int extent_range_uptodate(struct extent_io_tree *tree,
        return pg_uptodate;
 }
 
-int extent_buffer_uptodate(struct extent_io_tree *tree,
-                          struct extent_buffer *eb,
-                          struct extent_state *cached_state)
+int extent_buffer_uptodate(struct extent_buffer *eb)
 {
-       int ret = 0;
-       unsigned long num_pages;
-       unsigned long i;
-       struct page *page;
-       int pg_uptodate = 1;
-
-       if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
-               return 1;
-
-       if (eb_straddles_pages(eb)) {
-               ret = test_range_bit(tree, eb->start, eb->start + eb->len - 1,
-                                  EXTENT_UPTODATE, 1, cached_state);
-               if (ret)
-                       return ret;
-       }
-
-       num_pages = num_extent_pages(eb->start, eb->len);
-       for (i = 0; i < num_pages; i++) {
-               page = extent_buffer_page(eb, i);
-               if (!PageUptodate(page)) {
-                       pg_uptodate = 0;
-                       break;
-               }
-       }
-       return pg_uptodate;
+       return test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
 }
 
 int read_extent_buffer_pages(struct extent_io_tree *tree,
@@ -3981,21 +4420,14 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
        int ret = 0;
        int locked_pages = 0;
        int all_uptodate = 1;
-       int inc_all_pages = 0;
        unsigned long num_pages;
+       unsigned long num_reads = 0;
        struct bio *bio = NULL;
        unsigned long bio_flags = 0;
 
        if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
                return 0;
 
-       if (eb_straddles_pages(eb)) {
-               if (test_range_bit(tree, eb->start, eb->start + eb->len - 1,
-                                  EXTENT_UPTODATE, 1, NULL)) {
-                       return 0;
-               }
-       }
-
        if (start) {
                WARN_ON(start < eb->start);
                start_i = (start >> PAGE_CACHE_SHIFT) -
@@ -4014,8 +4446,10 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
                        lock_page(page);
                }
                locked_pages++;
-               if (!PageUptodate(page))
+               if (!PageUptodate(page)) {
+                       num_reads++;
                        all_uptodate = 0;
+               }
        }
        if (all_uptodate) {
                if (start_i == 0)
@@ -4023,20 +4457,12 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
                goto unlock_exit;
        }
 
+       clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
+       eb->failed_mirror = 0;
+       atomic_set(&eb->io_pages, num_reads);
        for (i = start_i; i < num_pages; i++) {
                page = extent_buffer_page(eb, i);
-
-               WARN_ON(!PagePrivate(page));
-
-               set_page_extent_mapped(page);
-               if (i == 0)
-                       set_page_extent_head(page, eb->len);
-
-               if (inc_all_pages)
-                       page_cache_get(page);
                if (!PageUptodate(page)) {
-                       if (start_i == 0)
-                               inc_all_pages = 1;
                        ClearPageError(page);
                        err = __extent_read_full_page(tree, page,
                                                      get_extent, &bio,
@@ -4048,8 +4474,11 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
                }
        }
 
-       if (bio)
-               submit_one_bio(READ, bio, mirror_num, bio_flags);
+       if (bio) {
+               err = submit_one_bio(READ, bio, mirror_num, bio_flags);
+               if (err)
+                       return err;
+       }
 
        if (ret || wait != WAIT_COMPLETE)
                return ret;
@@ -4061,8 +4490,6 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
                        ret = -EIO;
        }
 
-       if (!ret)
-               set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
        return ret;
 
 unlock_exit:
@@ -4304,15 +4731,20 @@ static void copy_pages(struct page *dst_page, struct page *src_page,
 {
        char *dst_kaddr = page_address(dst_page);
        char *src_kaddr;
+       int must_memmove = 0;
 
        if (dst_page != src_page) {
                src_kaddr = page_address(src_page);
        } else {
                src_kaddr = dst_kaddr;
-               BUG_ON(areas_overlap(src_off, dst_off, len));
+               if (areas_overlap(src_off, dst_off, len))
+                       must_memmove = 1;
        }
 
-       memcpy(dst_kaddr + dst_off, src_kaddr + src_off, len);
+       if (must_memmove)
+               memmove(dst_kaddr + dst_off, src_kaddr + src_off, len);
+       else
+               memcpy(dst_kaddr + dst_off, src_kaddr + src_off, len);
 }
 
 void memcpy_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
@@ -4382,7 +4814,7 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
                       "len %lu len %lu\n", dst_offset, len, dst->len);
                BUG_ON(1);
        }
-       if (!areas_overlap(src_offset, dst_offset, len)) {
+       if (dst_offset < src_offset) {
                memcpy_extent_buffer(dst, dst_offset, src_offset, len);
                return;
        }
@@ -4408,47 +4840,48 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
        }
 }
 
-static inline void btrfs_release_extent_buffer_rcu(struct rcu_head *head)
+int try_release_extent_buffer(struct page *page, gfp_t mask)
 {
-       struct extent_buffer *eb =
-                       container_of(head, struct extent_buffer, rcu_head);
-
-       btrfs_release_extent_buffer(eb);
-}
-
-int try_release_extent_buffer(struct extent_io_tree *tree, struct page *page)
-{
-       u64 start = page_offset(page);
        struct extent_buffer *eb;
-       int ret = 1;
 
-       spin_lock(&tree->buffer_lock);
-       eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT);
-       if (!eb) {
-               spin_unlock(&tree->buffer_lock);
-               return ret;
+       /*
+        * We need to make sure noboody is attaching this page to an eb right
+        * now.
+        */
+       spin_lock(&page->mapping->private_lock);
+       if (!PagePrivate(page)) {
+               spin_unlock(&page->mapping->private_lock);
+               return 1;
        }
 
-       if (test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
-               ret = 0;
-               goto out;
-       }
+       eb = (struct extent_buffer *)page->private;
+       BUG_ON(!eb);
 
        /*
-        * set @eb->refs to 0 if it is already 1, and then release the @eb.
-        * Or go back.
+        * This is a little awful but should be ok, we need to make sure that
+        * the eb doesn't disappear out from under us while we're looking at
+        * this page.
         */
-       if (atomic_cmpxchg(&eb->refs, 1, 0) != 1) {
-               ret = 0;
-               goto out;
+       spin_lock(&eb->refs_lock);
+       if (atomic_read(&eb->refs) != 1 || extent_buffer_under_io(eb)) {
+               spin_unlock(&eb->refs_lock);
+               spin_unlock(&page->mapping->private_lock);
+               return 0;
        }
+       spin_unlock(&page->mapping->private_lock);
 
-       radix_tree_delete(&tree->buffer, start >> PAGE_CACHE_SHIFT);
-out:
-       spin_unlock(&tree->buffer_lock);
+       if ((mask & GFP_NOFS) == GFP_NOFS)
+               mask = GFP_NOFS;
 
-       /* at this point we can safely release the extent buffer */
-       if (atomic_read(&eb->refs) == 0)
-               call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu);
-       return ret;
+       /*
+        * If tree ref isn't set then we know the ref on this eb is a real ref,
+        * so just return, this page will likely be freed soon anyway.
+        */
+       if (!test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags)) {
+               spin_unlock(&eb->refs_lock);
+               return 0;
+       }
+       release_extent_buffer(eb, mask);
+
+       return 1;
 }
index cecc3518c1213abb8704194fa6dff6f7662f5ad0..faf10eb57f75eb29edf1a8fb42468fb7924b666a 100644 (file)
 #define EXTENT_BUFFER_DIRTY 2
 #define EXTENT_BUFFER_CORRUPT 3
 #define EXTENT_BUFFER_READAHEAD 4      /* this got triggered by readahead */
+#define EXTENT_BUFFER_TREE_REF 5
+#define EXTENT_BUFFER_STALE 6
+#define EXTENT_BUFFER_WRITEBACK 7
+#define EXTENT_BUFFER_IOERR 8
 
 /* these are flags for extent_clear_unlock_delalloc */
 #define EXTENT_CLEAR_UNLOCK_PAGE 0x1
@@ -54,6 +58,7 @@
 #define EXTENT_PAGE_PRIVATE_FIRST_PAGE 3
 
 struct extent_state;
+struct btrfs_root;
 
 typedef        int (extent_submit_bio_hook_t)(struct inode *inode, int rw,
                                       struct bio *bio, int mirror_num,
@@ -69,9 +74,7 @@ struct extent_io_ops {
                              size_t size, struct bio *bio,
                              unsigned long bio_flags);
        int (*readpage_io_hook)(struct page *page, u64 start, u64 end);
-       int (*readpage_io_failed_hook)(struct bio *bio, struct page *page,
-                                      u64 start, u64 end, int failed_mirror,
-                                      struct extent_state *state);
+       int (*readpage_io_failed_hook)(struct page *page, int failed_mirror);
        int (*writepage_io_failed_hook)(struct bio *bio, struct page *page,
                                        u64 start, u64 end,
                                       struct extent_state *state);
@@ -97,6 +100,7 @@ struct extent_io_tree {
        struct radix_tree_root buffer;
        struct address_space *mapping;
        u64 dirty_bytes;
+       int track_uptodate;
        spinlock_t lock;
        spinlock_t buffer_lock;
        struct extent_io_ops *ops;
@@ -119,16 +123,21 @@ struct extent_state {
        struct list_head leak_list;
 };
 
+#define INLINE_EXTENT_BUFFER_PAGES 16
+#define MAX_INLINE_EXTENT_BUFFER_SIZE (INLINE_EXTENT_BUFFER_PAGES * PAGE_CACHE_SIZE)
 struct extent_buffer {
        u64 start;
        unsigned long len;
        unsigned long map_start;
        unsigned long map_len;
-       struct page *first_page;
        unsigned long bflags;
+       struct extent_io_tree *tree;
+       spinlock_t refs_lock;
+       atomic_t refs;
+       atomic_t io_pages;
+       int failed_mirror;
        struct list_head leak_list;
        struct rcu_head rcu_head;
-       atomic_t refs;
        pid_t lock_owner;
 
        /* count of read lock holders on the extent buffer */
@@ -152,6 +161,9 @@ struct extent_buffer {
         * to unlock
         */
        wait_queue_head_t read_lock_wq;
+       wait_queue_head_t lock_wq;
+       struct page *inline_pages[INLINE_EXTENT_BUFFER_PAGES];
+       struct page **pages;
 };
 
 static inline void extent_set_compress_type(unsigned long *bio_flags,
@@ -178,18 +190,17 @@ void extent_io_tree_init(struct extent_io_tree *tree,
 int try_release_extent_mapping(struct extent_map_tree *map,
                               struct extent_io_tree *tree, struct page *page,
                               gfp_t mask);
-int try_release_extent_buffer(struct extent_io_tree *tree, struct page *page);
+int try_release_extent_buffer(struct page *page, gfp_t mask);
 int try_release_extent_state(struct extent_map_tree *map,
                             struct extent_io_tree *tree, struct page *page,
                             gfp_t mask);
-int lock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask);
+int lock_extent(struct extent_io_tree *tree, u64 start, u64 end);
 int lock_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
-                    int bits, struct extent_state **cached, gfp_t mask);
-int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask);
+                    int bits, struct extent_state **cached);
+int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end);
 int unlock_extent_cached(struct extent_io_tree *tree, u64 start, u64 end,
                         struct extent_state **cached, gfp_t mask);
-int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end,
-                   gfp_t mask);
+int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end);
 int extent_read_full_page(struct extent_io_tree *tree, struct page *page,
                          get_extent_t *get_extent, int mirror_num);
 int __init extent_io_init(void);
@@ -210,7 +221,7 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
 int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
                    int bits, gfp_t mask);
 int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
-                  int bits, int exclusive_bits, u64 *failed_start,
+                  int bits, u64 *failed_start,
                   struct extent_state **cached_state, gfp_t mask);
 int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
                        struct extent_state **cached_state, gfp_t mask);
@@ -240,6 +251,8 @@ int extent_writepages(struct extent_io_tree *tree,
                      struct address_space *mapping,
                      get_extent_t *get_extent,
                      struct writeback_control *wbc);
+int btree_write_cache_pages(struct address_space *mapping,
+                           struct writeback_control *wbc);
 int extent_readpages(struct extent_io_tree *tree,
                     struct address_space *mapping,
                     struct list_head *pages, unsigned nr_pages,
@@ -251,11 +264,11 @@ int get_state_private(struct extent_io_tree *tree, u64 start, u64 *private);
 void set_page_extent_mapped(struct page *page);
 
 struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
-                                         u64 start, unsigned long len,
-                                         struct page *page0);
+                                         u64 start, unsigned long len);
 struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
                                         u64 start, unsigned long len);
 void free_extent_buffer(struct extent_buffer *eb);
+void free_extent_buffer_stale(struct extent_buffer *eb);
 #define WAIT_NONE      0
 #define WAIT_COMPLETE  1
 #define WAIT_PAGE_LOCK 2
@@ -287,19 +300,12 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset,
                           unsigned long src_offset, unsigned long len);
 void memset_extent_buffer(struct extent_buffer *eb, char c,
                          unsigned long start, unsigned long len);
-int wait_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits);
-int clear_extent_buffer_dirty(struct extent_io_tree *tree,
-                             struct extent_buffer *eb);
-int set_extent_buffer_dirty(struct extent_io_tree *tree,
-                            struct extent_buffer *eb);
-int set_extent_buffer_uptodate(struct extent_io_tree *tree,
-                              struct extent_buffer *eb);
-int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
-                               struct extent_buffer *eb,
-                               struct extent_state **cached_state);
-int extent_buffer_uptodate(struct extent_io_tree *tree,
-                          struct extent_buffer *eb,
-                          struct extent_state *cached_state);
+void wait_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits);
+void clear_extent_buffer_dirty(struct extent_buffer *eb);
+int set_extent_buffer_dirty(struct extent_buffer *eb);
+int set_extent_buffer_uptodate(struct extent_buffer *eb);
+int clear_extent_buffer_uptodate(struct extent_buffer *eb);
+int extent_buffer_uptodate(struct extent_buffer *eb);
 int map_private_extent_buffer(struct extent_buffer *eb, unsigned long offset,
                      unsigned long min_len, char **map,
                      unsigned long *map_start,
@@ -320,4 +326,6 @@ int repair_io_failure(struct btrfs_mapping_tree *map_tree, u64 start,
                        u64 length, u64 logical, struct page *page,
                        int mirror_num);
 int end_extent_writepage(struct page *page, int err, u64 start, u64 end);
+int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb,
+                        int mirror_num);
 #endif
index 078b4fd545000b627442100dfc76ef5df26228cc..5d158d3202331f87675b65f7de0fa7af419b80f2 100644 (file)
 #include "transaction.h"
 #include "print-tree.h"
 
-#define MAX_CSUM_ITEMS(r, size) ((((BTRFS_LEAF_DATA_SIZE(r) - \
+#define __MAX_CSUM_ITEMS(r, size) ((((BTRFS_LEAF_DATA_SIZE(r) - \
                                   sizeof(struct btrfs_item) * 2) / \
                                  size) - 1))
 
+#define MAX_CSUM_ITEMS(r, size) (min(__MAX_CSUM_ITEMS(r, size), PAGE_CACHE_SIZE))
+
 #define MAX_ORDERED_SUM_BYTES(r) ((PAGE_SIZE - \
                                   sizeof(struct btrfs_ordered_sum)) / \
                                   sizeof(struct btrfs_sector_sum) * \
@@ -59,7 +61,7 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
                                      sizeof(*item));
        if (ret < 0)
                goto out;
-       BUG_ON(ret);
+       BUG_ON(ret); /* Can't happen */
        leaf = path->nodes[0];
        item = btrfs_item_ptr(leaf, path->slots[0],
                              struct btrfs_file_extent_item);
@@ -284,6 +286,7 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
        struct btrfs_ordered_sum *sums;
        struct btrfs_sector_sum *sector_sum;
        struct btrfs_csum_item *item;
+       LIST_HEAD(tmplist);
        unsigned long offset;
        int ret;
        size_t size;
@@ -358,7 +361,10 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
                                        MAX_ORDERED_SUM_BYTES(root));
                        sums = kzalloc(btrfs_ordered_sum_size(root, size),
                                        GFP_NOFS);
-                       BUG_ON(!sums);
+                       if (!sums) {
+                               ret = -ENOMEM;
+                               goto fail;
+                       }
 
                        sector_sum = sums->sums;
                        sums->bytenr = start;
@@ -380,12 +386,19 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
                                offset += csum_size;
                                sector_sum++;
                        }
-                       list_add_tail(&sums->list, list);
+                       list_add_tail(&sums->list, &tmplist);
                }
                path->slots[0]++;
        }
        ret = 0;
 fail:
+       while (ret < 0 && !list_empty(&tmplist)) {
+               sums = list_entry(&tmplist, struct btrfs_ordered_sum, list);
+               list_del(&sums->list);
+               kfree(sums);
+       }
+       list_splice_tail(&tmplist, list);
+
        btrfs_free_path(path);
        return ret;
 }
@@ -420,7 +433,7 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
                offset = page_offset(bvec->bv_page) + bvec->bv_offset;
 
        ordered = btrfs_lookup_ordered_extent(inode, offset);
-       BUG_ON(!ordered);
+       BUG_ON(!ordered); /* Logic error */
        sums->bytenr = ordered->start;
 
        while (bio_index < bio->bi_vcnt) {
@@ -439,11 +452,11 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
 
                        sums = kzalloc(btrfs_ordered_sum_size(root, bytes_left),
                                       GFP_NOFS);
-                       BUG_ON(!sums);
+                       BUG_ON(!sums); /* -ENOMEM */
                        sector_sum = sums->sums;
                        sums->len = bytes_left;
                        ordered = btrfs_lookup_ordered_extent(inode, offset);
-                       BUG_ON(!ordered);
+                       BUG_ON(!ordered); /* Logic error */
                        sums->bytenr = ordered->start;
                }
 
@@ -483,18 +496,17 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
  * This calls btrfs_truncate_item with the correct args based on the
  * overlap, and fixes up the key as required.
  */
-static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
-                                     struct btrfs_root *root,
-                                     struct btrfs_path *path,
-                                     struct btrfs_key *key,
-                                     u64 bytenr, u64 len)
+static noinline void truncate_one_csum(struct btrfs_trans_handle *trans,
+                                      struct btrfs_root *root,
+                                      struct btrfs_path *path,
+                                      struct btrfs_key *key,
+                                      u64 bytenr, u64 len)
 {
        struct extent_buffer *leaf;
        u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
        u64 csum_end;
        u64 end_byte = bytenr + len;
        u32 blocksize_bits = root->fs_info->sb->s_blocksize_bits;
-       int ret;
 
        leaf = path->nodes[0];
        csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size;
@@ -510,7 +522,7 @@ static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
                 */
                u32 new_size = (bytenr - key->offset) >> blocksize_bits;
                new_size *= csum_size;
-               ret = btrfs_truncate_item(trans, root, path, new_size, 1);
+               btrfs_truncate_item(trans, root, path, new_size, 1);
        } else if (key->offset >= bytenr && csum_end > end_byte &&
                   end_byte > key->offset) {
                /*
@@ -522,15 +534,13 @@ static noinline int truncate_one_csum(struct btrfs_trans_handle *trans,
                u32 new_size = (csum_end - end_byte) >> blocksize_bits;
                new_size *= csum_size;
 
-               ret = btrfs_truncate_item(trans, root, path, new_size, 0);
+               btrfs_truncate_item(trans, root, path, new_size, 0);
 
                key->offset = end_byte;
-               ret = btrfs_set_item_key_safe(trans, root, path, key);
-               BUG_ON(ret);
+               btrfs_set_item_key_safe(trans, root, path, key);
        } else {
                BUG();
        }
-       return 0;
 }
 
 /*
@@ -635,13 +645,14 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
                         * item changed size or key
                         */
                        ret = btrfs_split_item(trans, root, path, &key, offset);
-                       BUG_ON(ret && ret != -EAGAIN);
+                       if (ret && ret != -EAGAIN) {
+                               btrfs_abort_transaction(trans, root, ret);
+                               goto out;
+                       }
 
                        key.offset = end_byte - 1;
                } else {
-                       ret = truncate_one_csum(trans, root, path,
-                                               &key, bytenr, len);
-                       BUG_ON(ret);
+                       truncate_one_csum(trans, root, path, &key, bytenr, len);
                        if (key.offset < bytenr)
                                break;
                }
@@ -772,7 +783,7 @@ again:
                if (diff != csum_size)
                        goto insert;
 
-               ret = btrfs_extend_item(trans, root, path, diff);
+               btrfs_extend_item(trans, root, path, diff);
                goto csum;
        }
 
index e8d06b6b9194af2839c686d9a651a56852876f96..d83260d7498fe2b535a59069ebba7c3ae78e255e 100644 (file)
@@ -452,7 +452,7 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
                        split = alloc_extent_map();
                if (!split2)
                        split2 = alloc_extent_map();
-               BUG_ON(!split || !split2);
+               BUG_ON(!split || !split2); /* -ENOMEM */
 
                write_lock(&em_tree->lock);
                em = lookup_extent_mapping(em_tree, start, len);
@@ -494,7 +494,7 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
                        split->flags = flags;
                        split->compress_type = em->compress_type;
                        ret = add_extent_mapping(em_tree, split);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* Logic error */
                        free_extent_map(split);
                        split = split2;
                        split2 = NULL;
@@ -520,7 +520,7 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
                        }
 
                        ret = add_extent_mapping(em_tree, split);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* Logic error */
                        free_extent_map(split);
                        split = NULL;
                }
@@ -679,7 +679,7 @@ next_slot:
                                                root->root_key.objectid,
                                                new_key.objectid,
                                                start - extent_offset, 0);
-                               BUG_ON(ret);
+                               BUG_ON(ret); /* -ENOMEM */
                                *hint_byte = disk_bytenr;
                        }
                        key.offset = start;
@@ -754,7 +754,7 @@ next_slot:
                                                root->root_key.objectid,
                                                key.objectid, key.offset -
                                                extent_offset, 0);
-                               BUG_ON(ret);
+                               BUG_ON(ret); /* -ENOMEM */
                                inode_sub_bytes(inode,
                                                extent_end - key.offset);
                                *hint_byte = disk_bytenr;
@@ -770,7 +770,10 @@ next_slot:
 
                        ret = btrfs_del_items(trans, root, path, del_slot,
                                              del_nr);
-                       BUG_ON(ret);
+                       if (ret) {
+                               btrfs_abort_transaction(trans, root, ret);
+                               goto out;
+                       }
 
                        del_nr = 0;
                        del_slot = 0;
@@ -782,11 +785,13 @@ next_slot:
                BUG_ON(1);
        }
 
-       if (del_nr > 0) {
+       if (!ret && del_nr > 0) {
                ret = btrfs_del_items(trans, root, path, del_slot, del_nr);
-               BUG_ON(ret);
+               if (ret)
+                       btrfs_abort_transaction(trans, root, ret);
        }
 
+out:
        btrfs_free_path(path);
        return ret;
 }
@@ -944,7 +949,10 @@ again:
                        btrfs_release_path(path);
                        goto again;
                }
-               BUG_ON(ret < 0);
+               if (ret < 0) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out;
+               }
 
                leaf = path->nodes[0];
                fi = btrfs_item_ptr(leaf, path->slots[0] - 1,
@@ -963,7 +971,7 @@ again:
                ret = btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, 0,
                                           root->root_key.objectid,
                                           ino, orig_offset, 0);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
 
                if (split == start) {
                        key.offset = start;
@@ -990,7 +998,7 @@ again:
                ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
                                        0, root->root_key.objectid,
                                        ino, orig_offset, 0);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
        other_start = 0;
        other_end = start;
@@ -1007,7 +1015,7 @@ again:
                ret = btrfs_free_extent(trans, root, bytenr, num_bytes,
                                        0, root->root_key.objectid,
                                        ino, orig_offset, 0);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
        if (del_nr == 0) {
                fi = btrfs_item_ptr(leaf, path->slots[0],
@@ -1025,7 +1033,10 @@ again:
                btrfs_mark_buffer_dirty(leaf);
 
                ret = btrfs_del_items(trans, root, path, del_slot, del_nr);
-               BUG_ON(ret);
+               if (ret < 0) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out;
+               }
        }
 out:
        btrfs_free_path(path);
@@ -1105,8 +1116,7 @@ again:
        if (start_pos < inode->i_size) {
                struct btrfs_ordered_extent *ordered;
                lock_extent_bits(&BTRFS_I(inode)->io_tree,
-                                start_pos, last_pos - 1, 0, &cached_state,
-                                GFP_NOFS);
+                                start_pos, last_pos - 1, 0, &cached_state);
                ordered = btrfs_lookup_first_ordered_extent(inode,
                                                            last_pos - 1);
                if (ordered &&
@@ -1638,7 +1648,7 @@ static long btrfs_fallocate(struct file *file, int mode,
                 * transaction
                 */
                lock_extent_bits(&BTRFS_I(inode)->io_tree, alloc_start,
-                                locked_end, 0, &cached_state, GFP_NOFS);
+                                locked_end, 0, &cached_state);
                ordered = btrfs_lookup_first_ordered_extent(inode,
                                                            alloc_end - 1);
                if (ordered &&
@@ -1667,7 +1677,13 @@ static long btrfs_fallocate(struct file *file, int mode,
 
                em = btrfs_get_extent(inode, NULL, 0, cur_offset,
                                      alloc_end - cur_offset, 0);
-               BUG_ON(IS_ERR_OR_NULL(em));
+               if (IS_ERR_OR_NULL(em)) {
+                       if (!em)
+                               ret = -ENOMEM;
+                       else
+                               ret = PTR_ERR(em);
+                       break;
+               }
                last_byte = min(extent_map_end(em), alloc_end);
                actual_end = min_t(u64, extent_map_end(em), offset + len);
                last_byte = (last_byte + mask) & ~mask;
@@ -1737,7 +1753,7 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int origin)
                return -ENXIO;
 
        lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0,
-                        &cached_state, GFP_NOFS);
+                        &cached_state);
 
        /*
         * Delalloc is such a pain.  If we have a hole and we have pending
index b02e379b14c7d5872513a52a05cea1d2d845c6b8..e88330d3df52f259b42e3fc5e0f865befd172e20 100644 (file)
@@ -230,11 +230,13 @@ int btrfs_truncate_free_space_cache(struct btrfs_root *root,
 
        if (ret) {
                trans->block_rsv = rsv;
-               WARN_ON(1);
+               btrfs_abort_transaction(trans, root, ret);
                return ret;
        }
 
        ret = btrfs_update_inode(trans, root, inode);
+       if (ret)
+               btrfs_abort_transaction(trans, root, ret);
        trans->block_rsv = rsv;
 
        return ret;
@@ -869,7 +871,7 @@ int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
        io_ctl_prepare_pages(&io_ctl, inode, 0);
 
        lock_extent_bits(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1,
-                        0, &cached_state, GFP_NOFS);
+                        0, &cached_state);
 
        node = rb_first(&ctl->free_space_offset);
        if (!node && cluster) {
@@ -1948,14 +1950,14 @@ again:
                 */
                ret = btrfs_add_free_space(block_group, old_start,
                                           offset - old_start);
-               WARN_ON(ret);
+               WARN_ON(ret); /* -ENOMEM */
                goto out;
        }
 
        ret = remove_from_bitmap(ctl, info, &offset, &bytes);
        if (ret == -EAGAIN)
                goto again;
-       BUG_ON(ret);
+       BUG_ON(ret); /* logic error */
 out_lock:
        spin_unlock(&ctl->tree_lock);
 out:
@@ -2346,7 +2348,7 @@ again:
        rb_erase(&entry->offset_index, &ctl->free_space_offset);
        ret = tree_insert_offset(&cluster->root, entry->offset,
                                 &entry->offset_index, 1);
-       BUG_ON(ret);
+       BUG_ON(ret); /* -EEXIST; Logic error */
 
        trace_btrfs_setup_cluster(block_group, cluster,
                                  total_found * block_group->sectorsize, 1);
@@ -2439,7 +2441,7 @@ setup_cluster_no_bitmap(struct btrfs_block_group_cache *block_group,
                ret = tree_insert_offset(&cluster->root, entry->offset,
                                         &entry->offset_index, 0);
                total_size += entry->bytes;
-               BUG_ON(ret);
+               BUG_ON(ret); /* -EEXIST; Logic error */
        } while (node && entry != last);
 
        cluster->max_size = max_extent;
@@ -2830,6 +2832,7 @@ u64 btrfs_find_ino_for_alloc(struct btrfs_root *fs_root)
                int ret;
 
                ret = search_bitmap(ctl, entry, &offset, &count);
+               /* Logic error; Should be empty if it can't find anything */
                BUG_ON(ret);
 
                ino = offset;
index baa74f3db6911fb4ff6f2497964a2f69caef95a0..a13cf1a96c73ca00f4baa453f864029c03525fe4 100644 (file)
@@ -19,6 +19,7 @@
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
+#include "print-tree.h"
 
 static int find_name_in_backref(struct btrfs_path *path, const char *name,
                         int name_len, struct btrfs_inode_ref **ref_ret)
@@ -128,13 +129,14 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
        item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
        memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
                              item_size - (ptr + sub_item_len - item_start));
-       ret = btrfs_truncate_item(trans, root, path,
+       btrfs_truncate_item(trans, root, path,
                                  item_size - sub_item_len, 1);
 out:
        btrfs_free_path(path);
        return ret;
 }
 
+/* Will return 0, -ENOMEM, -EMLINK, or -EEXIST or anything from the CoW path */
 int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root,
                           const char *name, int name_len,
@@ -165,7 +167,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
                        goto out;
 
                old_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]);
-               ret = btrfs_extend_item(trans, root, path, ins_len);
+               btrfs_extend_item(trans, root, path, ins_len);
                ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
                                     struct btrfs_inode_ref);
                ref = (struct btrfs_inode_ref *)((unsigned long)ref + old_size);
index ee15d88b33d2a3dca65f41a4c5c3f6a36bbe5b48..b1a1c929ba8047553aa36773cafaf692b75eb2f7 100644 (file)
@@ -178,7 +178,7 @@ static void start_caching(struct btrfs_root *root)
 
        tsk = kthread_run(caching_kthread, root, "btrfs-ino-cache-%llu\n",
                          root->root_key.objectid);
-       BUG_ON(IS_ERR(tsk));
+       BUG_ON(IS_ERR(tsk)); /* -ENOMEM */
 }
 
 int btrfs_find_free_ino(struct btrfs_root *root, u64 *objectid)
@@ -271,7 +271,7 @@ void btrfs_unpin_free_ino(struct btrfs_root *root)
                        break;
 
                info = rb_entry(n, struct btrfs_free_space, offset_index);
-               BUG_ON(info->bitmap);
+               BUG_ON(info->bitmap); /* Logic error */
 
                if (info->offset > root->cache_progress)
                        goto free;
@@ -439,17 +439,16 @@ int btrfs_save_ino_cache(struct btrfs_root *root,
        if (ret)
                goto out;
        trace_btrfs_space_reservation(root->fs_info, "ino_cache",
-                                     (u64)(unsigned long)trans,
-                                     trans->bytes_reserved, 1);
+                                     trans->transid, trans->bytes_reserved, 1);
 again:
        inode = lookup_free_ino_inode(root, path);
-       if (IS_ERR(inode) && PTR_ERR(inode) != -ENOENT) {
+       if (IS_ERR(inode) && (PTR_ERR(inode) != -ENOENT || retry)) {
                ret = PTR_ERR(inode);
                goto out_release;
        }
 
        if (IS_ERR(inode)) {
-               BUG_ON(retry);
+               BUG_ON(retry); /* Logic error */
                retry = true;
 
                ret = create_free_ino_inode(root, trans, path);
@@ -460,12 +459,17 @@ again:
 
        BTRFS_I(inode)->generation = 0;
        ret = btrfs_update_inode(trans, root, inode);
-       WARN_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto out_put;
+       }
 
        if (i_size_read(inode) > 0) {
                ret = btrfs_truncate_free_space_cache(root, trans, path, inode);
-               if (ret)
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
                        goto out_put;
+               }
        }
 
        spin_lock(&root->cache_lock);
@@ -502,8 +506,7 @@ out_put:
        iput(inode);
 out_release:
        trace_btrfs_space_reservation(root->fs_info, "ino_cache",
-                                     (u64)(unsigned long)trans,
-                                     trans->bytes_reserved, 0);
+                                     trans->transid, trans->bytes_reserved, 0);
        btrfs_block_rsv_release(root, trans->block_rsv, trans->bytes_reserved);
 out:
        trans->block_rsv = rsv;
@@ -532,7 +535,7 @@ static int btrfs_find_highest_objectid(struct btrfs_root *root, u64 *objectid)
        ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
        if (ret < 0)
                goto error;
-       BUG_ON(ret == 0);
+       BUG_ON(ret == 0); /* Corruption */
        if (path->slots[0] > 0) {
                slot = path->slots[0] - 1;
                l = path->nodes[0];
index 3a0b5c1f9d315c07a30b1399a1baa39e4f7b27dd..115bc05e42b06fee05ef7551cef1ad4174557634 100644 (file)
@@ -150,7 +150,6 @@ static noinline int insert_inline_extent(struct btrfs_trans_handle *trans,
        inode_add_bytes(inode, size);
        ret = btrfs_insert_empty_item(trans, root, path, &key,
                                      datasize);
-       BUG_ON(ret);
        if (ret) {
                err = ret;
                goto fail;
@@ -206,9 +205,9 @@ static noinline int insert_inline_extent(struct btrfs_trans_handle *trans,
         * could end up racing with unlink.
         */
        BTRFS_I(inode)->disk_i_size = inode->i_size;
-       btrfs_update_inode(trans, root, inode);
+       ret = btrfs_update_inode(trans, root, inode);
 
-       return 0;
+       return ret;
 fail:
        btrfs_free_path(path);
        return err;
@@ -250,14 +249,18 @@ static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans,
 
        ret = btrfs_drop_extents(trans, inode, start, aligned_end,
                                 &hint_byte, 1);
-       BUG_ON(ret);
+       if (ret)
+               return ret;
 
        if (isize > actual_end)
                inline_len = min_t(u64, isize, actual_end);
        ret = insert_inline_extent(trans, root, inode, start,
                                   inline_len, compressed_size,
                                   compress_type, compressed_pages);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               return ret;
+       }
        btrfs_delalloc_release_metadata(inode, end + 1 - start);
        btrfs_drop_extent_cache(inode, start, aligned_end - 1, 0);
        return 0;
@@ -293,7 +296,7 @@ static noinline int add_async_extent(struct async_cow *cow,
        struct async_extent *async_extent;
 
        async_extent = kmalloc(sizeof(*async_extent), GFP_NOFS);
-       BUG_ON(!async_extent);
+       BUG_ON(!async_extent); /* -ENOMEM */
        async_extent->start = start;
        async_extent->ram_size = ram_size;
        async_extent->compressed_size = compressed_size;
@@ -344,8 +347,9 @@ static noinline int compress_file_range(struct inode *inode,
        int will_compress;
        int compress_type = root->fs_info->compress_type;
 
-       /* if this is a small write inside eof, kick off a defragbot */
-       if (end <= BTRFS_I(inode)->disk_i_size && (end - start + 1) < 16 * 1024)
+       /* if this is a small write inside eof, kick off a defrag */
+       if ((end - start + 1) < 16 * 1024 &&
+           (start > 0 || end + 1 < BTRFS_I(inode)->disk_i_size))
                btrfs_add_inode_defrag(NULL, inode);
 
        actual_end = min_t(u64, isize, end + 1);
@@ -433,7 +437,11 @@ again:
 cont:
        if (start == 0) {
                trans = btrfs_join_transaction(root);
-               BUG_ON(IS_ERR(trans));
+               if (IS_ERR(trans)) {
+                       ret = PTR_ERR(trans);
+                       trans = NULL;
+                       goto cleanup_and_out;
+               }
                trans->block_rsv = &root->fs_info->delalloc_block_rsv;
 
                /* lets try to make an inline extent */
@@ -450,11 +458,11 @@ cont:
                                                    total_compressed,
                                                    compress_type, pages);
                }
-               if (ret == 0) {
+               if (ret <= 0) {
                        /*
-                        * inline extent creation worked, we don't need
-                        * to create any more async work items.  Unlock
-                        * and free up our temp pages.
+                        * inline extent creation worked or returned error,
+                        * we don't need to create any more async work items.
+                        * Unlock and free up our temp pages.
                         */
                        extent_clear_unlock_delalloc(inode,
                             &BTRFS_I(inode)->io_tree,
@@ -547,7 +555,7 @@ cleanup_and_bail_uncompressed:
        }
 
 out:
-       return 0;
+       return ret;
 
 free_pages_out:
        for (i = 0; i < nr_pages_ret; i++) {
@@ -557,6 +565,20 @@ free_pages_out:
        kfree(pages);
 
        goto out;
+
+cleanup_and_out:
+       extent_clear_unlock_delalloc(inode, &BTRFS_I(inode)->io_tree,
+                                    start, end, NULL,
+                                    EXTENT_CLEAR_UNLOCK_PAGE |
+                                    EXTENT_CLEAR_DIRTY |
+                                    EXTENT_CLEAR_DELALLOC |
+                                    EXTENT_SET_WRITEBACK |
+                                    EXTENT_END_WRITEBACK);
+       if (!trans || IS_ERR(trans))
+               btrfs_error(root->fs_info, ret, "Failed to join transaction");
+       else
+               btrfs_abort_transaction(trans, root, ret);
+       goto free_pages_out;
 }
 
 /*
@@ -597,7 +619,7 @@ retry:
 
                        lock_extent(io_tree, async_extent->start,
                                         async_extent->start +
-                                        async_extent->ram_size - 1, GFP_NOFS);
+                                        async_extent->ram_size - 1);
 
                        /* allocate blocks */
                        ret = cow_file_range(inode, async_cow->locked_page,
@@ -606,6 +628,8 @@ retry:
                                             async_extent->ram_size - 1,
                                             &page_started, &nr_written, 0);
 
+                       /* JDM XXX */
+
                        /*
                         * if page_started, cow_file_range inserted an
                         * inline extent and took care of all the unlocking
@@ -625,18 +649,21 @@ retry:
                }
 
                lock_extent(io_tree, async_extent->start,
-                           async_extent->start + async_extent->ram_size - 1,
-                           GFP_NOFS);
+                           async_extent->start + async_extent->ram_size - 1);
 
                trans = btrfs_join_transaction(root);
-               BUG_ON(IS_ERR(trans));
-               trans->block_rsv = &root->fs_info->delalloc_block_rsv;
-               ret = btrfs_reserve_extent(trans, root,
+               if (IS_ERR(trans)) {
+                       ret = PTR_ERR(trans);
+               } else {
+                       trans->block_rsv = &root->fs_info->delalloc_block_rsv;
+                       ret = btrfs_reserve_extent(trans, root,
                                           async_extent->compressed_size,
                                           async_extent->compressed_size,
-                                          0, alloc_hint,
-                                          (u64)-1, &ins, 1);
-               btrfs_end_transaction(trans, root);
+                                          0, alloc_hint, &ins, 1);
+                       if (ret)
+                               btrfs_abort_transaction(trans, root, ret);
+                       btrfs_end_transaction(trans, root);
+               }
 
                if (ret) {
                        int i;
@@ -649,8 +676,10 @@ retry:
                        async_extent->pages = NULL;
                        unlock_extent(io_tree, async_extent->start,
                                      async_extent->start +
-                                     async_extent->ram_size - 1, GFP_NOFS);
-                       goto retry;
+                                     async_extent->ram_size - 1);
+                       if (ret == -ENOSPC)
+                               goto retry;
+                       goto out_free; /* JDM: Requeue? */
                }
 
                /*
@@ -662,7 +691,7 @@ retry:
                                        async_extent->ram_size - 1, 0);
 
                em = alloc_extent_map();
-               BUG_ON(!em);
+               BUG_ON(!em); /* -ENOMEM */
                em->start = async_extent->start;
                em->len = async_extent->ram_size;
                em->orig_start = em->start;
@@ -694,7 +723,7 @@ retry:
                                                ins.offset,
                                                BTRFS_ORDERED_COMPRESSED,
                                                async_extent->compress_type);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
 
                /*
                 * clear dirty, set writeback and unlock the pages.
@@ -716,13 +745,17 @@ retry:
                                    ins.offset, async_extent->pages,
                                    async_extent->nr_pages);
 
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
                alloc_hint = ins.objectid + ins.offset;
                kfree(async_extent);
                cond_resched();
        }
-
-       return 0;
+       ret = 0;
+out:
+       return ret;
+out_free:
+       kfree(async_extent);
+       goto out;
 }
 
 static u64 get_extent_allocation_hint(struct inode *inode, u64 start,
@@ -791,7 +824,18 @@ static noinline int cow_file_range(struct inode *inode,
 
        BUG_ON(btrfs_is_free_space_inode(root, inode));
        trans = btrfs_join_transaction(root);
-       BUG_ON(IS_ERR(trans));
+       if (IS_ERR(trans)) {
+               extent_clear_unlock_delalloc(inode,
+                            &BTRFS_I(inode)->io_tree,
+                            start, end, NULL,
+                            EXTENT_CLEAR_UNLOCK_PAGE |
+                            EXTENT_CLEAR_UNLOCK |
+                            EXTENT_CLEAR_DELALLOC |
+                            EXTENT_CLEAR_DIRTY |
+                            EXTENT_SET_WRITEBACK |
+                            EXTENT_END_WRITEBACK);
+               return PTR_ERR(trans);
+       }
        trans->block_rsv = &root->fs_info->delalloc_block_rsv;
 
        num_bytes = (end - start + blocksize) & ~(blocksize - 1);
@@ -800,7 +844,8 @@ static noinline int cow_file_range(struct inode *inode,
        ret = 0;
 
        /* if this is a small write inside eof, kick off defrag */
-       if (end <= BTRFS_I(inode)->disk_i_size && num_bytes < 64 * 1024)
+       if (num_bytes < 64 * 1024 &&
+           (start > 0 || end + 1 < BTRFS_I(inode)->disk_i_size))
                btrfs_add_inode_defrag(trans, inode);
 
        if (start == 0) {
@@ -821,8 +866,10 @@ static noinline int cow_file_range(struct inode *inode,
                        *nr_written = *nr_written +
                             (end - start + PAGE_CACHE_SIZE) / PAGE_CACHE_SIZE;
                        *page_started = 1;
-                       ret = 0;
                        goto out;
+               } else if (ret < 0) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out_unlock;
                }
        }
 
@@ -838,11 +885,14 @@ static noinline int cow_file_range(struct inode *inode,
                cur_alloc_size = disk_num_bytes;
                ret = btrfs_reserve_extent(trans, root, cur_alloc_size,
                                           root->sectorsize, 0, alloc_hint,
-                                          (u64)-1, &ins, 1);
-               BUG_ON(ret);
+                                          &ins, 1);
+               if (ret < 0) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out_unlock;
+               }
 
                em = alloc_extent_map();
-               BUG_ON(!em);
+               BUG_ON(!em); /* -ENOMEM */
                em->start = start;
                em->orig_start = em->start;
                ram_size = ins.offset;
@@ -868,13 +918,16 @@ static noinline int cow_file_range(struct inode *inode,
                cur_alloc_size = ins.offset;
                ret = btrfs_add_ordered_extent(inode, start, ins.objectid,
                                               ram_size, cur_alloc_size, 0);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
 
                if (root->root_key.objectid ==
                    BTRFS_DATA_RELOC_TREE_OBJECTID) {
                        ret = btrfs_reloc_clone_csums(inode, start,
                                                      cur_alloc_size);
-                       BUG_ON(ret);
+                       if (ret) {
+                               btrfs_abort_transaction(trans, root, ret);
+                               goto out_unlock;
+                       }
                }
 
                if (disk_num_bytes < cur_alloc_size)
@@ -899,11 +952,23 @@ static noinline int cow_file_range(struct inode *inode,
                alloc_hint = ins.objectid + ins.offset;
                start += cur_alloc_size;
        }
-out:
        ret = 0;
+out:
        btrfs_end_transaction(trans, root);
 
        return ret;
+out_unlock:
+       extent_clear_unlock_delalloc(inode,
+                    &BTRFS_I(inode)->io_tree,
+                    start, end, NULL,
+                    EXTENT_CLEAR_UNLOCK_PAGE |
+                    EXTENT_CLEAR_UNLOCK |
+                    EXTENT_CLEAR_DELALLOC |
+                    EXTENT_CLEAR_DIRTY |
+                    EXTENT_SET_WRITEBACK |
+                    EXTENT_END_WRITEBACK);
+
+       goto out;
 }
 
 /*
@@ -969,7 +1034,7 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
                         1, 0, NULL, GFP_NOFS);
        while (start < end) {
                async_cow = kmalloc(sizeof(*async_cow), GFP_NOFS);
-               BUG_ON(!async_cow);
+               BUG_ON(!async_cow); /* -ENOMEM */
                async_cow->inode = inode;
                async_cow->root = root;
                async_cow->locked_page = locked_page;
@@ -1060,7 +1125,7 @@ static noinline int run_delalloc_nocow(struct inode *inode,
        u64 disk_bytenr;
        u64 num_bytes;
        int extent_type;
-       int ret;
+       int ret, err;
        int type;
        int nocow;
        int check_prev = 1;
@@ -1078,7 +1143,11 @@ static noinline int run_delalloc_nocow(struct inode *inode,
        else
                trans = btrfs_join_transaction(root);
 
-       BUG_ON(IS_ERR(trans));
+       if (IS_ERR(trans)) {
+               btrfs_free_path(path);
+               return PTR_ERR(trans);
+       }
+
        trans->block_rsv = &root->fs_info->delalloc_block_rsv;
 
        cow_start = (u64)-1;
@@ -1086,7 +1155,10 @@ static noinline int run_delalloc_nocow(struct inode *inode,
        while (1) {
                ret = btrfs_lookup_file_extent(trans, root, path, ino,
                                               cur_offset, 0);
-               BUG_ON(ret < 0);
+               if (ret < 0) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto error;
+               }
                if (ret > 0 && path->slots[0] > 0 && check_prev) {
                        leaf = path->nodes[0];
                        btrfs_item_key_to_cpu(leaf, &found_key,
@@ -1100,8 +1172,10 @@ next_slot:
                leaf = path->nodes[0];
                if (path->slots[0] >= btrfs_header_nritems(leaf)) {
                        ret = btrfs_next_leaf(root, path);
-                       if (ret < 0)
-                               BUG_ON(1);
+                       if (ret < 0) {
+                               btrfs_abort_transaction(trans, root, ret);
+                               goto error;
+                       }
                        if (ret > 0)
                                break;
                        leaf = path->nodes[0];
@@ -1189,7 +1263,10 @@ out_check:
                        ret = cow_file_range(inode, locked_page, cow_start,
                                        found_key.offset - 1, page_started,
                                        nr_written, 1);
-                       BUG_ON(ret);
+                       if (ret) {
+                               btrfs_abort_transaction(trans, root, ret);
+                               goto error;
+                       }
                        cow_start = (u64)-1;
                }
 
@@ -1198,7 +1275,7 @@ out_check:
                        struct extent_map_tree *em_tree;
                        em_tree = &BTRFS_I(inode)->extent_tree;
                        em = alloc_extent_map();
-                       BUG_ON(!em);
+                       BUG_ON(!em); /* -ENOMEM */
                        em->start = cur_offset;
                        em->orig_start = em->start;
                        em->len = num_bytes;
@@ -1224,13 +1301,16 @@ out_check:
 
                ret = btrfs_add_ordered_extent(inode, cur_offset, disk_bytenr,
                                               num_bytes, num_bytes, type);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
 
                if (root->root_key.objectid ==
                    BTRFS_DATA_RELOC_TREE_OBJECTID) {
                        ret = btrfs_reloc_clone_csums(inode, cur_offset,
                                                      num_bytes);
-                       BUG_ON(ret);
+                       if (ret) {
+                               btrfs_abort_transaction(trans, root, ret);
+                               goto error;
+                       }
                }
 
                extent_clear_unlock_delalloc(inode, &BTRFS_I(inode)->io_tree,
@@ -1249,18 +1329,23 @@ out_check:
        if (cow_start != (u64)-1) {
                ret = cow_file_range(inode, locked_page, cow_start, end,
                                     page_started, nr_written, 1);
-               BUG_ON(ret);
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto error;
+               }
        }
 
+error:
        if (nolock) {
-               ret = btrfs_end_transaction_nolock(trans, root);
-               BUG_ON(ret);
+               err = btrfs_end_transaction_nolock(trans, root);
        } else {
-               ret = btrfs_end_transaction(trans, root);
-               BUG_ON(ret);
+               err = btrfs_end_transaction(trans, root);
        }
+       if (!ret)
+               ret = err;
+
        btrfs_free_path(path);
-       return 0;
+       return ret;
 }
 
 /*
@@ -1425,10 +1510,11 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
        map_length = length;
        ret = btrfs_map_block(map_tree, READ, logical,
                              &map_length, NULL, 0);
-
+       /* Will always return 0 or 1 with map_multi == NULL */
+       BUG_ON(ret < 0);
        if (map_length < length + size)
                return 1;
-       return ret;
+       return 0;
 }
 
 /*
@@ -1448,7 +1534,7 @@ static int __btrfs_submit_bio_start(struct inode *inode, int rw,
        int ret = 0;
 
        ret = btrfs_csum_one_bio(root, inode, bio, 0, 0);
-       BUG_ON(ret);
+       BUG_ON(ret); /* -ENOMEM */
        return 0;
 }
 
@@ -1479,14 +1565,16 @@ static int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
        struct btrfs_root *root = BTRFS_I(inode)->root;
        int ret = 0;
        int skip_sum;
+       int metadata = 0;
 
        skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
 
        if (btrfs_is_free_space_inode(root, inode))
-               ret = btrfs_bio_wq_end_io(root->fs_info, bio, 2);
-       else
-               ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
-       BUG_ON(ret);
+               metadata = 2;
+
+       ret = btrfs_bio_wq_end_io(root->fs_info, bio, metadata);
+       if (ret)
+               return ret;
 
        if (!(rw & REQ_WRITE)) {
                if (bio_flags & EXTENT_BIO_COMPRESSED) {
@@ -1571,7 +1659,7 @@ again:
        page_end = page_offset(page) + PAGE_CACHE_SIZE - 1;
 
        lock_extent_bits(&BTRFS_I(inode)->io_tree, page_start, page_end, 0,
-                        &cached_state, GFP_NOFS);
+                        &cached_state);
 
        /* already ordered? We're done */
        if (PagePrivate2(page))
@@ -1675,13 +1763,15 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
         */
        ret = btrfs_drop_extents(trans, inode, file_pos, file_pos + num_bytes,
                                 &hint, 0);
-       BUG_ON(ret);
+       if (ret)
+               goto out;
 
        ins.objectid = btrfs_ino(inode);
        ins.offset = file_pos;
        ins.type = BTRFS_EXTENT_DATA_KEY;
        ret = btrfs_insert_empty_item(trans, root, path, &ins, sizeof(*fi));
-       BUG_ON(ret);
+       if (ret)
+               goto out;
        leaf = path->nodes[0];
        fi = btrfs_item_ptr(leaf, path->slots[0],
                            struct btrfs_file_extent_item);
@@ -1709,10 +1799,10 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
        ret = btrfs_alloc_reserved_file_extent(trans, root,
                                        root->root_key.objectid,
                                        btrfs_ino(inode), file_pos, &ins);
-       BUG_ON(ret);
+out:
        btrfs_free_path(path);
 
-       return 0;
+       return ret;
 }
 
 /*
@@ -1740,35 +1830,41 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
                                             end - start + 1);
        if (!ret)
                return 0;
-       BUG_ON(!ordered_extent);
+       BUG_ON(!ordered_extent); /* Logic error */
 
        nolock = btrfs_is_free_space_inode(root, inode);
 
        if (test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags)) {
-               BUG_ON(!list_empty(&ordered_extent->list));
+               BUG_ON(!list_empty(&ordered_extent->list)); /* Logic error */
                ret = btrfs_ordered_update_i_size(inode, 0, ordered_extent);
                if (!ret) {
                        if (nolock)
                                trans = btrfs_join_transaction_nolock(root);
                        else
                                trans = btrfs_join_transaction(root);
-                       BUG_ON(IS_ERR(trans));
+                       if (IS_ERR(trans))
+                               return PTR_ERR(trans);
                        trans->block_rsv = &root->fs_info->delalloc_block_rsv;
                        ret = btrfs_update_inode_fallback(trans, root, inode);
-                       BUG_ON(ret);
+                       if (ret) /* -ENOMEM or corruption */
+                               btrfs_abort_transaction(trans, root, ret);
                }
                goto out;
        }
 
        lock_extent_bits(io_tree, ordered_extent->file_offset,
                         ordered_extent->file_offset + ordered_extent->len - 1,
-                        0, &cached_state, GFP_NOFS);
+                        0, &cached_state);
 
        if (nolock)
                trans = btrfs_join_transaction_nolock(root);
        else
                trans = btrfs_join_transaction(root);
-       BUG_ON(IS_ERR(trans));
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               trans = NULL;
+               goto out_unlock;
+       }
        trans->block_rsv = &root->fs_info->delalloc_block_rsv;
 
        if (test_bit(BTRFS_ORDERED_COMPRESSED, &ordered_extent->flags))
@@ -1779,7 +1875,6 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
                                                ordered_extent->file_offset,
                                                ordered_extent->file_offset +
                                                ordered_extent->len);
-               BUG_ON(ret);
        } else {
                BUG_ON(root == root->fs_info->tree_root);
                ret = insert_reserved_file_extent(trans, inode,
@@ -1793,11 +1888,14 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
                unpin_extent_cache(&BTRFS_I(inode)->extent_tree,
                                   ordered_extent->file_offset,
                                   ordered_extent->len);
-               BUG_ON(ret);
        }
        unlock_extent_cached(io_tree, ordered_extent->file_offset,
                             ordered_extent->file_offset +
                             ordered_extent->len - 1, &cached_state, GFP_NOFS);
+       if (ret < 0) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto out;
+       }
 
        add_pending_csums(trans, inode, ordered_extent->file_offset,
                          &ordered_extent->list);
@@ -1805,7 +1903,10 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
        ret = btrfs_ordered_update_i_size(inode, 0, ordered_extent);
        if (!ret || !test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) {
                ret = btrfs_update_inode_fallback(trans, root, inode);
-               BUG_ON(ret);
+               if (ret) { /* -ENOMEM or corruption */
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out;
+               }
        }
        ret = 0;
 out:
@@ -1824,6 +1925,11 @@ out:
        btrfs_put_ordered_extent(ordered_extent);
 
        return 0;
+out_unlock:
+       unlock_extent_cached(io_tree, ordered_extent->file_offset,
+                            ordered_extent->file_offset +
+                            ordered_extent->len - 1, &cached_state, GFP_NOFS);
+       goto out;
 }
 
 static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
@@ -1905,6 +2011,8 @@ struct delayed_iput {
        struct inode *inode;
 };
 
+/* JDM: If this is fs-wide, why can't we add a pointer to
+ * btrfs_inode instead and avoid the allocation? */
 void btrfs_add_delayed_iput(struct inode *inode)
 {
        struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
@@ -2051,20 +2159,27 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode)
        /* grab metadata reservation from transaction handle */
        if (reserve) {
                ret = btrfs_orphan_reserve_metadata(trans, inode);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOSPC in reservation; Logic error? JDM */
        }
 
        /* insert an orphan item to track this unlinked/truncated file */
        if (insert >= 1) {
                ret = btrfs_insert_orphan_item(trans, root, btrfs_ino(inode));
-               BUG_ON(ret && ret != -EEXIST);
+               if (ret && ret != -EEXIST) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       return ret;
+               }
+               ret = 0;
        }
 
        /* insert an orphan item to track subvolume contains orphan files */
        if (insert >= 2) {
                ret = btrfs_insert_orphan_item(trans, root->fs_info->tree_root,
                                               root->root_key.objectid);
-               BUG_ON(ret);
+               if (ret && ret != -EEXIST) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       return ret;
+               }
        }
        return 0;
 }
@@ -2094,7 +2209,7 @@ int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode)
 
        if (trans && delete_item) {
                ret = btrfs_del_orphan_item(trans, root, btrfs_ino(inode));
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM or corruption (JDM: Recheck) */
        }
 
        if (release_rsv)
@@ -2228,7 +2343,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
                        }
                        ret = btrfs_del_orphan_item(trans, root,
                                                    found_key.objectid);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM or corruption (JDM: Recheck) */
                        btrfs_end_transaction(trans, root);
                        continue;
                }
@@ -2610,16 +2725,22 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
                printk(KERN_INFO "btrfs failed to delete reference to %.*s, "
                       "inode %llu parent %llu\n", name_len, name,
                       (unsigned long long)ino, (unsigned long long)dir_ino);
+               btrfs_abort_transaction(trans, root, ret);
                goto err;
        }
 
        ret = btrfs_delete_delayed_dir_index(trans, root, dir, index);
-       if (ret)
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
                goto err;
+       }
 
        ret = btrfs_del_inode_ref_in_log(trans, root, name, name_len,
                                         inode, dir_ino);
-       BUG_ON(ret != 0 && ret != -ENOENT);
+       if (ret != 0 && ret != -ENOENT) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto err;
+       }
 
        ret = btrfs_del_dir_entries_in_log(trans, root, name, name_len,
                                           dir, index);
@@ -2777,7 +2898,7 @@ static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir,
                        err = ret;
                        goto out;
                }
-               BUG_ON(ret == 0);
+               BUG_ON(ret == 0); /* Corruption */
                if (check_path_shared(root, path))
                        goto out;
                btrfs_release_path(path);
@@ -2810,7 +2931,7 @@ static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir,
                err = PTR_ERR(ref);
                goto out;
        }
-       BUG_ON(!ref);
+       BUG_ON(!ref); /* Logic error */
        if (check_path_shared(root, path))
                goto out;
        index = btrfs_inode_ref_index(path->nodes[0], ref);
@@ -2917,23 +3038,42 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
 
        di = btrfs_lookup_dir_item(trans, root, path, dir_ino,
                                   name, name_len, -1);
-       BUG_ON(IS_ERR_OR_NULL(di));
+       if (IS_ERR_OR_NULL(di)) {
+               if (!di)
+                       ret = -ENOENT;
+               else
+                       ret = PTR_ERR(di);
+               goto out;
+       }
 
        leaf = path->nodes[0];
        btrfs_dir_item_key_to_cpu(leaf, di, &key);
        WARN_ON(key.type != BTRFS_ROOT_ITEM_KEY || key.objectid != objectid);
        ret = btrfs_delete_one_dir_name(trans, root, path, di);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto out;
+       }
        btrfs_release_path(path);
 
        ret = btrfs_del_root_ref(trans, root->fs_info->tree_root,
                                 objectid, root->root_key.objectid,
                                 dir_ino, &index, name, name_len);
        if (ret < 0) {
-               BUG_ON(ret != -ENOENT);
+               if (ret != -ENOENT) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out;
+               }
                di = btrfs_search_dir_index_item(root, path, dir_ino,
                                                 name, name_len);
-               BUG_ON(IS_ERR_OR_NULL(di));
+               if (IS_ERR_OR_NULL(di)) {
+                       if (!di)
+                               ret = -ENOENT;
+                       else
+                               ret = PTR_ERR(di);
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out;
+               }
 
                leaf = path->nodes[0];
                btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
@@ -2943,15 +3083,19 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
        btrfs_release_path(path);
 
        ret = btrfs_delete_delayed_dir_index(trans, root, dir, index);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto out;
+       }
 
        btrfs_i_size_write(dir, dir->i_size - name_len * 2);
        dir->i_mtime = dir->i_ctime = CURRENT_TIME;
        ret = btrfs_update_inode(trans, root, dir);
-       BUG_ON(ret);
-
+       if (ret)
+               btrfs_abort_transaction(trans, root, ret);
+out:
        btrfs_free_path(path);
-       return 0;
+       return ret;
 }
 
 static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
@@ -3161,8 +3305,8 @@ search_again:
                                }
                                size =
                                    btrfs_file_extent_calc_inline_size(size);
-                               ret = btrfs_truncate_item(trans, root, path,
-                                                         size, 1);
+                               btrfs_truncate_item(trans, root, path,
+                                                   size, 1);
                        } else if (root->ref_cows) {
                                inode_sub_bytes(inode, item_end + 1 -
                                                found_key.offset);
@@ -3210,7 +3354,11 @@ delete:
                                ret = btrfs_del_items(trans, root, path,
                                                pending_del_slot,
                                                pending_del_nr);
-                               BUG_ON(ret);
+                               if (ret) {
+                                       btrfs_abort_transaction(trans,
+                                                               root, ret);
+                                       goto error;
+                               }
                                pending_del_nr = 0;
                        }
                        btrfs_release_path(path);
@@ -3223,8 +3371,10 @@ out:
        if (pending_del_nr) {
                ret = btrfs_del_items(trans, root, path, pending_del_slot,
                                      pending_del_nr);
-               BUG_ON(ret);
+               if (ret)
+                       btrfs_abort_transaction(trans, root, ret);
        }
+error:
        btrfs_free_path(path);
        return err;
 }
@@ -3282,8 +3432,7 @@ again:
        }
        wait_on_page_writeback(page);
 
-       lock_extent_bits(io_tree, page_start, page_end, 0, &cached_state,
-                        GFP_NOFS);
+       lock_extent_bits(io_tree, page_start, page_end, 0, &cached_state);
        set_page_extent_mapped(page);
 
        ordered = btrfs_lookup_ordered_extent(inode, page_start);
@@ -3359,7 +3508,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
                btrfs_wait_ordered_range(inode, hole_start,
                                         block_end - hole_start);
                lock_extent_bits(io_tree, hole_start, block_end - 1, 0,
-                                &cached_state, GFP_NOFS);
+                                &cached_state);
                ordered = btrfs_lookup_ordered_extent(inode, hole_start);
                if (!ordered)
                        break;
@@ -3372,7 +3521,10 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
        while (1) {
                em = btrfs_get_extent(inode, NULL, 0, cur_offset,
                                block_end - cur_offset, 0);
-               BUG_ON(IS_ERR_OR_NULL(em));
+               if (IS_ERR(em)) {
+                       err = PTR_ERR(em);
+                       break;
+               }
                last_byte = min(extent_map_end(em), block_end);
                last_byte = (last_byte + mask) & ~mask;
                if (!test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) {
@@ -3389,7 +3541,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
                                                 cur_offset + hole_size,
                                                 &hint_byte, 1);
                        if (err) {
-                               btrfs_update_inode(trans, root, inode);
+                               btrfs_abort_transaction(trans, root, err);
                                btrfs_end_transaction(trans, root);
                                break;
                        }
@@ -3399,7 +3551,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
                                        0, hole_size, 0, hole_size,
                                        0, 0, 0);
                        if (err) {
-                               btrfs_update_inode(trans, root, inode);
+                               btrfs_abort_transaction(trans, root, err);
                                btrfs_end_transaction(trans, root);
                                break;
                        }
@@ -3779,7 +3931,7 @@ static void inode_tree_del(struct inode *inode)
        }
 }
 
-int btrfs_invalidate_inodes(struct btrfs_root *root)
+void btrfs_invalidate_inodes(struct btrfs_root *root)
 {
        struct rb_node *node;
        struct rb_node *prev;
@@ -3839,7 +3991,6 @@ again:
                node = rb_next(node);
        }
        spin_unlock(&root->inode_lock);
-       return 0;
 }
 
 static int btrfs_init_locked_inode(struct inode *inode, void *p)
@@ -4581,18 +4732,26 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
                                             parent_ino, index);
        }
 
-       if (ret == 0) {
-               ret = btrfs_insert_dir_item(trans, root, name, name_len,
-                                           parent_inode, &key,
-                                           btrfs_inode_type(inode), index);
-               if (ret)
-                       goto fail_dir_item;
+       /* Nothing to clean up yet */
+       if (ret)
+               return ret;
 
-               btrfs_i_size_write(parent_inode, parent_inode->i_size +
-                                  name_len * 2);
-               parent_inode->i_mtime = parent_inode->i_ctime = CURRENT_TIME;
-               ret = btrfs_update_inode(trans, root, parent_inode);
+       ret = btrfs_insert_dir_item(trans, root, name, name_len,
+                                   parent_inode, &key,
+                                   btrfs_inode_type(inode), index);
+       if (ret == -EEXIST)
+               goto fail_dir_item;
+       else if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               return ret;
        }
+
+       btrfs_i_size_write(parent_inode, parent_inode->i_size +
+                          name_len * 2);
+       parent_inode->i_mtime = parent_inode->i_ctime = CURRENT_TIME;
+       ret = btrfs_update_inode(trans, root, parent_inode);
+       if (ret)
+               btrfs_abort_transaction(trans, root, ret);
        return ret;
 
 fail_dir_item:
@@ -4806,7 +4965,8 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
        } else {
                struct dentry *parent = dentry->d_parent;
                err = btrfs_update_inode(trans, root, inode);
-               BUG_ON(err);
+               if (err)
+                       goto fail;
                d_instantiate(dentry, inode);
                btrfs_log_new_name(trans, inode, NULL, parent);
        }
@@ -5137,7 +5297,7 @@ again:
                                ret = uncompress_inline(path, inode, page,
                                                        pg_offset,
                                                        extent_offset, item);
-                               BUG_ON(ret);
+                               BUG_ON(ret); /* -ENOMEM */
                        } else {
                                map = kmap(page);
                                read_extent_buffer(leaf, map + pg_offset, ptr,
@@ -5252,6 +5412,7 @@ out:
                free_extent_map(em);
                return ERR_PTR(err);
        }
+       BUG_ON(!em); /* Error is always set */
        return em;
 }
 
@@ -5414,7 +5575,7 @@ static struct extent_map *btrfs_new_extent_direct(struct inode *inode,
 
        alloc_hint = get_extent_allocation_hint(inode, start, len);
        ret = btrfs_reserve_extent(trans, root, len, root->sectorsize, 0,
-                                  alloc_hint, (u64)-1, &ins, 1);
+                                  alloc_hint, &ins, 1);
        if (ret) {
                em = ERR_PTR(ret);
                goto out;
@@ -5602,7 +5763,7 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
                free_extent_map(em);
                /* DIO will do one hole at a time, so just unlock a sector */
                unlock_extent(&BTRFS_I(inode)->io_tree, start,
-                             start + root->sectorsize - 1, GFP_NOFS);
+                             start + root->sectorsize - 1);
                return 0;
        }
 
@@ -5743,7 +5904,7 @@ static void btrfs_endio_direct_read(struct bio *bio, int err)
        } while (bvec <= bvec_end);
 
        unlock_extent(&BTRFS_I(inode)->io_tree, dip->logical_offset,
-                     dip->logical_offset + dip->bytes - 1, GFP_NOFS);
+                     dip->logical_offset + dip->bytes - 1);
        bio->bi_private = dip->private;
 
        kfree(dip->csums);
@@ -5794,7 +5955,7 @@ again:
 
        lock_extent_bits(&BTRFS_I(inode)->io_tree, ordered->file_offset,
                         ordered->file_offset + ordered->len - 1, 0,
-                        &cached_state, GFP_NOFS);
+                        &cached_state);
 
        if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags)) {
                ret = btrfs_mark_extent_written(trans, inode,
@@ -5868,7 +6029,7 @@ static int __btrfs_submit_bio_start_direct_io(struct inode *inode, int rw,
        int ret;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        ret = btrfs_csum_one_bio(root, inode, bio, offset, 1);
-       BUG_ON(ret);
+       BUG_ON(ret); /* -ENOMEM */
        return 0;
 }
 
@@ -6209,7 +6370,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
 
        while (1) {
                lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend,
-                                0, &cached_state, GFP_NOFS);
+                                0, &cached_state);
                /*
                 * We're concerned with the entire range that we're going to be
                 * doing DIO to, so we need to make sure theres no ordered
@@ -6233,7 +6394,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
        if (writing) {
                write_bits = EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING;
                ret = set_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
-                                    EXTENT_DELALLOC, 0, NULL, &cached_state,
+                                    EXTENT_DELALLOC, NULL, &cached_state,
                                     GFP_NOFS);
                if (ret) {
                        clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart,
@@ -6363,8 +6524,7 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset)
                btrfs_releasepage(page, GFP_NOFS);
                return;
        }
-       lock_extent_bits(tree, page_start, page_end, 0, &cached_state,
-                        GFP_NOFS);
+       lock_extent_bits(tree, page_start, page_end, 0, &cached_state);
        ordered = btrfs_lookup_ordered_extent(page->mapping->host,
                                           page_offset(page));
        if (ordered) {
@@ -6386,8 +6546,7 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset)
                }
                btrfs_put_ordered_extent(ordered);
                cached_state = NULL;
-               lock_extent_bits(tree, page_start, page_end, 0, &cached_state,
-                                GFP_NOFS);
+               lock_extent_bits(tree, page_start, page_end, 0, &cached_state);
        }
        clear_extent_bit(tree, page_start, page_end,
                 EXTENT_LOCKED | EXTENT_DIRTY | EXTENT_DELALLOC |
@@ -6462,8 +6621,7 @@ again:
        }
        wait_on_page_writeback(page);
 
-       lock_extent_bits(io_tree, page_start, page_end, 0, &cached_state,
-                        GFP_NOFS);
+       lock_extent_bits(io_tree, page_start, page_end, 0, &cached_state);
        set_page_extent_mapped(page);
 
        /*
@@ -6737,10 +6895,9 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
        btrfs_i_size_write(inode, 0);
 
        err = btrfs_update_inode(trans, new_root, inode);
-       BUG_ON(err);
 
        iput(inode);
-       return 0;
+       return err;
 }
 
 struct inode *btrfs_alloc_inode(struct super_block *sb)
@@ -6783,6 +6940,8 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
        extent_map_tree_init(&ei->extent_tree);
        extent_io_tree_init(&ei->io_tree, &inode->i_data);
        extent_io_tree_init(&ei->io_failure_tree, &inode->i_data);
+       ei->io_tree.track_uptodate = 1;
+       ei->io_failure_tree.track_uptodate = 1;
        mutex_init(&ei->log_mutex);
        mutex_init(&ei->delalloc_mutex);
        btrfs_ordered_inode_tree_init(&ei->ordered_tree);
@@ -7072,7 +7231,10 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                if (!ret)
                        ret = btrfs_update_inode(trans, root, old_inode);
        }
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto out_fail;
+       }
 
        if (new_inode) {
                new_inode->i_ctime = CURRENT_TIME;
@@ -7090,11 +7252,14 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                                                 new_dentry->d_name.name,
                                                 new_dentry->d_name.len);
                }
-               BUG_ON(ret);
-               if (new_inode->i_nlink == 0) {
+               if (!ret && new_inode->i_nlink == 0) {
                        ret = btrfs_orphan_add(trans, new_dentry->d_inode);
                        BUG_ON(ret);
                }
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       goto out_fail;
+               }
        }
 
        fixup_inode_flags(new_dir, old_inode);
@@ -7102,7 +7267,10 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        ret = btrfs_add_link(trans, new_dir, old_inode,
                             new_dentry->d_name.name,
                             new_dentry->d_name.len, 0, index);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto out_fail;
+       }
 
        if (old_ino != BTRFS_FIRST_FREE_OBJECTID) {
                struct dentry *parent = new_dentry->d_parent;
@@ -7315,7 +7483,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
                }
 
                ret = btrfs_reserve_extent(trans, root, num_bytes, min_size,
-                                          0, *alloc_hint, (u64)-1, &ins, 1);
+                                          0, *alloc_hint, &ins, 1);
                if (ret) {
                        if (own_trans)
                                btrfs_end_transaction(trans, root);
@@ -7327,7 +7495,12 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
                                                  ins.offset, ins.offset,
                                                  ins.offset, 0, 0, 0,
                                                  BTRFS_FILE_EXTENT_PREALLOC);
-               BUG_ON(ret);
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       if (own_trans)
+                               btrfs_end_transaction(trans, root);
+                       break;
+               }
                btrfs_drop_extent_cache(inode, cur_offset,
                                        cur_offset + ins.offset -1, 0);
 
@@ -7349,7 +7522,13 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
                }
 
                ret = btrfs_update_inode(trans, root, inode);
-               BUG_ON(ret);
+
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       if (own_trans)
+                               btrfs_end_transaction(trans, root);
+                       break;
+               }
 
                if (own_trans)
                        btrfs_end_transaction(trans, root);
index d8b54715c2deb655508229a7f4120e1141c8daae..18cc23d164a80b7ccc717a83499214b81f395896 100644 (file)
@@ -425,22 +425,37 @@ static noinline int create_subvol(struct btrfs_root *root,
 
        key.offset = (u64)-1;
        new_root = btrfs_read_fs_root_no_name(root->fs_info, &key);
-       BUG_ON(IS_ERR(new_root));
+       if (IS_ERR(new_root)) {
+               btrfs_abort_transaction(trans, root, PTR_ERR(new_root));
+               ret = PTR_ERR(new_root);
+               goto fail;
+       }
 
        btrfs_record_root_in_trans(trans, new_root);
 
        ret = btrfs_create_subvol_root(trans, new_root, new_dirid);
+       if (ret) {
+               /* We potentially lose an unused inode item here */
+               btrfs_abort_transaction(trans, root, ret);
+               goto fail;
+       }
+
        /*
         * insert the directory item
         */
        ret = btrfs_set_inode_index(dir, &index);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto fail;
+       }
 
        ret = btrfs_insert_dir_item(trans, root,
                                    name, namelen, dir, &key,
                                    BTRFS_FT_DIR, index);
-       if (ret)
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
                goto fail;
+       }
 
        btrfs_i_size_write(dir, dir->i_size + namelen * 2);
        ret = btrfs_update_inode(trans, root, dir);
@@ -769,6 +784,31 @@ none:
        return -ENOENT;
 }
 
+/*
+ * Validaty check of prev em and next em:
+ * 1) no prev/next em
+ * 2) prev/next em is an hole/inline extent
+ */
+static int check_adjacent_extents(struct inode *inode, struct extent_map *em)
+{
+       struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
+       struct extent_map *prev = NULL, *next = NULL;
+       int ret = 0;
+
+       read_lock(&em_tree->lock);
+       prev = lookup_extent_mapping(em_tree, em->start - 1, (u64)-1);
+       next = lookup_extent_mapping(em_tree, em->start + em->len, (u64)-1);
+       read_unlock(&em_tree->lock);
+
+       if ((!prev || prev->block_start >= EXTENT_MAP_LAST_BYTE) &&
+           (!next || next->block_start >= EXTENT_MAP_LAST_BYTE))
+               ret = 1;
+       free_extent_map(prev);
+       free_extent_map(next);
+
+       return ret;
+}
+
 static int should_defrag_range(struct inode *inode, u64 start, u64 len,
                               int thresh, u64 *last_len, u64 *skip,
                               u64 *defrag_end)
@@ -797,17 +837,25 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
 
        if (!em) {
                /* get the big lock and read metadata off disk */
-               lock_extent(io_tree, start, start + len - 1, GFP_NOFS);
+               lock_extent(io_tree, start, start + len - 1);
                em = btrfs_get_extent(inode, NULL, 0, start, len, 0);
-               unlock_extent(io_tree, start, start + len - 1, GFP_NOFS);
+               unlock_extent(io_tree, start, start + len - 1);
 
                if (IS_ERR(em))
                        return 0;
        }
 
        /* this will cover holes, and inline extents */
-       if (em->block_start >= EXTENT_MAP_LAST_BYTE)
+       if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
                ret = 0;
+               goto out;
+       }
+
+       /* If we have nothing to merge with us, just skip. */
+       if (check_adjacent_extents(inode, em)) {
+               ret = 0;
+               goto out;
+       }
 
        /*
         * we hit a real extent, if it is big don't bother defragging it again
@@ -815,6 +863,7 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
        if ((*last_len == 0 || *last_len >= thresh) && em->len >= thresh)
                ret = 0;
 
+out:
        /*
         * last_len ends up being a counter of how many bytes we've defragged.
         * every time we choose not to defrag an extent, we reset *last_len
@@ -856,6 +905,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
        u64 isize = i_size_read(inode);
        u64 page_start;
        u64 page_end;
+       u64 page_cnt;
        int ret;
        int i;
        int i_done;
@@ -864,19 +914,21 @@ static int cluster_pages_for_defrag(struct inode *inode,
        struct extent_io_tree *tree;
        gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping);
 
-       if (isize == 0)
-               return 0;
        file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
+       if (!isize || start_index > file_end)
+               return 0;
+
+       page_cnt = min_t(u64, (u64)num_pages, (u64)file_end - start_index + 1);
 
        ret = btrfs_delalloc_reserve_space(inode,
-                                          num_pages << PAGE_CACHE_SHIFT);
+                                          page_cnt << PAGE_CACHE_SHIFT);
        if (ret)
                return ret;
        i_done = 0;
        tree = &BTRFS_I(inode)->io_tree;
 
        /* step one, lock all the pages */
-       for (i = 0; i < num_pages; i++) {
+       for (i = 0; i < page_cnt; i++) {
                struct page *page;
 again:
                page = find_or_create_page(inode->i_mapping,
@@ -887,10 +939,10 @@ again:
                page_start = page_offset(page);
                page_end = page_start + PAGE_CACHE_SIZE - 1;
                while (1) {
-                       lock_extent(tree, page_start, page_end, GFP_NOFS);
+                       lock_extent(tree, page_start, page_end);
                        ordered = btrfs_lookup_ordered_extent(inode,
                                                              page_start);
-                       unlock_extent(tree, page_start, page_end, GFP_NOFS);
+                       unlock_extent(tree, page_start, page_end);
                        if (!ordered)
                                break;
 
@@ -898,6 +950,15 @@ again:
                        btrfs_start_ordered_extent(inode, ordered, 1);
                        btrfs_put_ordered_extent(ordered);
                        lock_page(page);
+                       /*
+                        * we unlocked the page above, so we need check if
+                        * it was released or not.
+                        */
+                       if (page->mapping != inode->i_mapping) {
+                               unlock_page(page);
+                               page_cache_release(page);
+                               goto again;
+                       }
                }
 
                if (!PageUptodate(page)) {
@@ -911,15 +972,6 @@ again:
                        }
                }
 
-               isize = i_size_read(inode);
-               file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
-               if (!isize || page->index > file_end) {
-                       /* whoops, we blew past eof, skip this page */
-                       unlock_page(page);
-                       page_cache_release(page);
-                       break;
-               }
-
                if (page->mapping != inode->i_mapping) {
                        unlock_page(page);
                        page_cache_release(page);
@@ -946,19 +998,18 @@ again:
        page_end = page_offset(pages[i_done - 1]) + PAGE_CACHE_SIZE;
 
        lock_extent_bits(&BTRFS_I(inode)->io_tree,
-                        page_start, page_end - 1, 0, &cached_state,
-                        GFP_NOFS);
+                        page_start, page_end - 1, 0, &cached_state);
        clear_extent_bit(&BTRFS_I(inode)->io_tree, page_start,
                          page_end - 1, EXTENT_DIRTY | EXTENT_DELALLOC |
                          EXTENT_DO_ACCOUNTING, 0, 0, &cached_state,
                          GFP_NOFS);
 
-       if (i_done != num_pages) {
+       if (i_done != page_cnt) {
                spin_lock(&BTRFS_I(inode)->lock);
                BTRFS_I(inode)->outstanding_extents++;
                spin_unlock(&BTRFS_I(inode)->lock);
                btrfs_delalloc_release_space(inode,
-                                    (num_pages - i_done) << PAGE_CACHE_SHIFT);
+                                    (page_cnt - i_done) << PAGE_CACHE_SHIFT);
        }
 
 
@@ -983,7 +1034,7 @@ out:
                unlock_page(pages[i]);
                page_cache_release(pages[i]);
        }
-       btrfs_delalloc_release_space(inode, num_pages << PAGE_CACHE_SHIFT);
+       btrfs_delalloc_release_space(inode, page_cnt << PAGE_CACHE_SHIFT);
        return ret;
 
 }
@@ -1089,12 +1140,9 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
                if (!(inode->i_sb->s_flags & MS_ACTIVE))
                        break;
 
-               if (!newer_than &&
-                   !should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,
-                                       PAGE_CACHE_SIZE,
-                                       extent_thresh,
-                                       &last_len, &skip,
-                                       &defrag_end)) {
+               if (!should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,
+                                        PAGE_CACHE_SIZE, extent_thresh,
+                                        &last_len, &skip, &defrag_end)) {
                        unsigned long next;
                        /*
                         * the should_defrag function tells us how much to skip
@@ -1123,17 +1171,24 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
                        ra_index += max_cluster;
                }
 
+               mutex_lock(&inode->i_mutex);
                ret = cluster_pages_for_defrag(inode, pages, i, cluster);
-               if (ret < 0)
+               if (ret < 0) {
+                       mutex_unlock(&inode->i_mutex);
                        goto out_ra;
+               }
 
                defrag_count += ret;
                balance_dirty_pages_ratelimited_nr(inode->i_mapping, ret);
+               mutex_unlock(&inode->i_mutex);
 
                if (newer_than) {
                        if (newer_off == (u64)-1)
                                break;
 
+                       if (ret > 0)
+                               i += ret;
+
                        newer_off = max(newer_off + 1,
                                        (u64)i << PAGE_CACHE_SHIFT);
 
@@ -1966,7 +2021,11 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
                                dest->root_key.objectid,
                                dentry->d_name.name,
                                dentry->d_name.len);
-       BUG_ON(ret);
+       if (ret) {
+               err = ret;
+               btrfs_abort_transaction(trans, root, ret);
+               goto out_end_trans;
+       }
 
        btrfs_record_root_in_trans(trans, dest);
 
@@ -1979,11 +2038,16 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
                ret = btrfs_insert_orphan_item(trans,
                                        root->fs_info->tree_root,
                                        dest->root_key.objectid);
-               BUG_ON(ret);
+               if (ret) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       err = ret;
+                       goto out_end_trans;
+               }
        }
-
+out_end_trans:
        ret = btrfs_end_transaction(trans, root);
-       BUG_ON(ret);
+       if (ret && !err)
+               err = ret;
        inode->i_flags |= S_DEAD;
 out_up_write:
        up_write(&root->fs_info->subvol_sem);
@@ -2326,13 +2390,13 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
           another, and lock file content */
        while (1) {
                struct btrfs_ordered_extent *ordered;
-               lock_extent(&BTRFS_I(src)->io_tree, off, off+len, GFP_NOFS);
+               lock_extent(&BTRFS_I(src)->io_tree, off, off+len);
                ordered = btrfs_lookup_first_ordered_extent(src, off+len);
                if (!ordered &&
                    !test_range_bit(&BTRFS_I(src)->io_tree, off, off+len,
                                   EXTENT_DELALLOC, 0, NULL))
                        break;
-               unlock_extent(&BTRFS_I(src)->io_tree, off, off+len, GFP_NOFS);
+               unlock_extent(&BTRFS_I(src)->io_tree, off, off+len);
                if (ordered)
                        btrfs_put_ordered_extent(ordered);
                btrfs_wait_ordered_range(src, off, len);
@@ -2447,11 +2511,21 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                                                         new_key.offset,
                                                         new_key.offset + datal,
                                                         &hint_byte, 1);
-                               BUG_ON(ret);
+                               if (ret) {
+                                       btrfs_abort_transaction(trans, root,
+                                                               ret);
+                                       btrfs_end_transaction(trans, root);
+                                       goto out;
+                               }
 
                                ret = btrfs_insert_empty_item(trans, root, path,
                                                              &new_key, size);
-                               BUG_ON(ret);
+                               if (ret) {
+                                       btrfs_abort_transaction(trans, root,
+                                                               ret);
+                                       btrfs_end_transaction(trans, root);
+                                       goto out;
+                               }
 
                                leaf = path->nodes[0];
                                slot = path->slots[0];
@@ -2478,7 +2552,15 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                                                        btrfs_ino(inode),
                                                        new_key.offset - datao,
                                                        0);
-                                       BUG_ON(ret);
+                                       if (ret) {
+                                               btrfs_abort_transaction(trans,
+                                                                       root,
+                                                                       ret);
+                                               btrfs_end_transaction(trans,
+                                                                     root);
+                                               goto out;
+
+                                       }
                                }
                        } else if (type == BTRFS_FILE_EXTENT_INLINE) {
                                u64 skip = 0;
@@ -2503,11 +2585,21 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                                                         new_key.offset,
                                                         new_key.offset + datal,
                                                         &hint_byte, 1);
-                               BUG_ON(ret);
+                               if (ret) {
+                                       btrfs_abort_transaction(trans, root,
+                                                               ret);
+                                       btrfs_end_transaction(trans, root);
+                                       goto out;
+                               }
 
                                ret = btrfs_insert_empty_item(trans, root, path,
                                                              &new_key, size);
-                               BUG_ON(ret);
+                               if (ret) {
+                                       btrfs_abort_transaction(trans, root,
+                                                               ret);
+                                       btrfs_end_transaction(trans, root);
+                                       goto out;
+                               }
 
                                if (skip) {
                                        u32 start =
@@ -2541,8 +2633,12 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                                btrfs_i_size_write(inode, endoff);
 
                        ret = btrfs_update_inode(trans, root, inode);
-                       BUG_ON(ret);
-                       btrfs_end_transaction(trans, root);
+                       if (ret) {
+                               btrfs_abort_transaction(trans, root, ret);
+                               btrfs_end_transaction(trans, root);
+                               goto out;
+                       }
+                       ret = btrfs_end_transaction(trans, root);
                }
 next:
                btrfs_release_path(path);
@@ -2551,7 +2647,7 @@ next:
        ret = 0;
 out:
        btrfs_release_path(path);
-       unlock_extent(&BTRFS_I(src)->io_tree, off, off+len, GFP_NOFS);
+       unlock_extent(&BTRFS_I(src)->io_tree, off, off+len);
 out_unlock:
        mutex_unlock(&src->i_mutex);
        mutex_unlock(&inode->i_mutex);
@@ -3066,8 +3162,8 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_root *root,
                goto out;
 
        extent_item_pos = loi->logical - key.objectid;
-       ret = iterate_extent_inodes(root->fs_info, path, key.objectid,
-                                       extent_item_pos, build_ino_list,
+       ret = iterate_extent_inodes(root->fs_info, key.objectid,
+                                       extent_item_pos, 0, build_ino_list,
                                        inodes);
 
        if (ret < 0)
index 5e178d8f7167f496e928613b6c1f0000c2ea242e..272f911203ffc61a66b3db9ffe8a9a3b9502ea24 100644 (file)
@@ -208,7 +208,7 @@ void btrfs_tree_read_unlock_blocking(struct extent_buffer *eb)
  * take a spinning write lock.  This will wait for both
  * blocking readers or writers
  */
-int btrfs_tree_lock(struct extent_buffer *eb)
+void btrfs_tree_lock(struct extent_buffer *eb)
 {
 again:
        wait_event(eb->read_lock_wq, atomic_read(&eb->blocking_readers) == 0);
@@ -230,13 +230,12 @@ again:
        atomic_inc(&eb->spinning_writers);
        atomic_inc(&eb->write_locks);
        eb->lock_owner = current->pid;
-       return 0;
 }
 
 /*
  * drop a spinning or a blocking write lock.
  */
-int btrfs_tree_unlock(struct extent_buffer *eb)
+void btrfs_tree_unlock(struct extent_buffer *eb)
 {
        int blockers = atomic_read(&eb->blocking_writers);
 
@@ -255,7 +254,6 @@ int btrfs_tree_unlock(struct extent_buffer *eb)
                atomic_dec(&eb->spinning_writers);
                write_unlock(&eb->lock);
        }
-       return 0;
 }
 
 void btrfs_assert_tree_locked(struct extent_buffer *eb)
index 17247ddb81a00f80e2e3e0a82c5c87e36cc564d1..ca52681e5f4049c45f2a228a3f8237ca23815772 100644 (file)
@@ -24,8 +24,8 @@
 #define BTRFS_WRITE_LOCK_BLOCKING 3
 #define BTRFS_READ_LOCK_BLOCKING 4
 
-int btrfs_tree_lock(struct extent_buffer *eb);
-int btrfs_tree_unlock(struct extent_buffer *eb);
+void btrfs_tree_lock(struct extent_buffer *eb);
+void btrfs_tree_unlock(struct extent_buffer *eb);
 int btrfs_try_spin_lock(struct extent_buffer *eb);
 
 void btrfs_tree_read_lock(struct extent_buffer *eb);
index a1c94042530787539350c72e9449c314a8899e9e..bbf6d0d9aebe9b68f0ea8e5c121783d81733f7d7 100644 (file)
@@ -59,6 +59,14 @@ static struct rb_node *tree_insert(struct rb_root *root, u64 file_offset,
        return NULL;
 }
 
+static void ordered_data_tree_panic(struct inode *inode, int errno,
+                                              u64 offset)
+{
+       struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
+       btrfs_panic(fs_info, errno, "Inconsistency in ordered tree at offset "
+                   "%llu\n", (unsigned long long)offset);
+}
+
 /*
  * look for a given offset in the tree, and if it can't be found return the
  * first lesser offset
@@ -207,7 +215,8 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
        spin_lock(&tree->lock);
        node = tree_insert(&tree->tree, file_offset,
                           &entry->rb_node);
-       BUG_ON(node);
+       if (node)
+               ordered_data_tree_panic(inode, -EEXIST, file_offset);
        spin_unlock(&tree->lock);
 
        spin_lock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
@@ -215,7 +224,6 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
                      &BTRFS_I(inode)->root->fs_info->ordered_extents);
        spin_unlock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
 
-       BUG_ON(node);
        return 0;
 }
 
@@ -249,9 +257,9 @@ int btrfs_add_ordered_extent_compress(struct inode *inode, u64 file_offset,
  * when an ordered extent is finished.  If the list covers more than one
  * ordered extent, it is split across multiples.
  */
-int btrfs_add_ordered_sum(struct inode *inode,
-                         struct btrfs_ordered_extent *entry,
-                         struct btrfs_ordered_sum *sum)
+void btrfs_add_ordered_sum(struct inode *inode,
+                          struct btrfs_ordered_extent *entry,
+                          struct btrfs_ordered_sum *sum)
 {
        struct btrfs_ordered_inode_tree *tree;
 
@@ -259,7 +267,6 @@ int btrfs_add_ordered_sum(struct inode *inode,
        spin_lock(&tree->lock);
        list_add_tail(&sum->list, &entry->list);
        spin_unlock(&tree->lock);
-       return 0;
 }
 
 /*
@@ -384,7 +391,7 @@ out:
  * used to drop a reference on an ordered extent.  This will free
  * the extent if the last reference is dropped
  */
-int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
+void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
 {
        struct list_head *cur;
        struct btrfs_ordered_sum *sum;
@@ -400,7 +407,6 @@ int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
                }
                kfree(entry);
        }
-       return 0;
 }
 
 /*
@@ -408,8 +414,8 @@ int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
  * and you must wake_up entry->wait.  You must hold the tree lock
  * while you call this function.
  */
-static int __btrfs_remove_ordered_extent(struct inode *inode,
-                               struct btrfs_ordered_extent *entry)
+static void __btrfs_remove_ordered_extent(struct inode *inode,
+                                         struct btrfs_ordered_extent *entry)
 {
        struct btrfs_ordered_inode_tree *tree;
        struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -436,35 +442,30 @@ static int __btrfs_remove_ordered_extent(struct inode *inode,
                list_del_init(&BTRFS_I(inode)->ordered_operations);
        }
        spin_unlock(&root->fs_info->ordered_extent_lock);
-
-       return 0;
 }
 
 /*
  * remove an ordered extent from the tree.  No references are dropped
  * but any waiters are woken.
  */
-int btrfs_remove_ordered_extent(struct inode *inode,
-                               struct btrfs_ordered_extent *entry)
+void btrfs_remove_ordered_extent(struct inode *inode,
+                                struct btrfs_ordered_extent *entry)
 {
        struct btrfs_ordered_inode_tree *tree;
-       int ret;
 
        tree = &BTRFS_I(inode)->ordered_tree;
        spin_lock(&tree->lock);
-       ret = __btrfs_remove_ordered_extent(inode, entry);
+       __btrfs_remove_ordered_extent(inode, entry);
        spin_unlock(&tree->lock);
        wake_up(&entry->wait);
-
-       return ret;
 }
 
 /*
  * wait for all the ordered extents in a root.  This is done when balancing
  * space between drives.
  */
-int btrfs_wait_ordered_extents(struct btrfs_root *root,
-                              int nocow_only, int delay_iput)
+void btrfs_wait_ordered_extents(struct btrfs_root *root,
+                               int nocow_only, int delay_iput)
 {
        struct list_head splice;
        struct list_head *cur;
@@ -512,7 +513,6 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root,
                spin_lock(&root->fs_info->ordered_extent_lock);
        }
        spin_unlock(&root->fs_info->ordered_extent_lock);
-       return 0;
 }
 
 /*
@@ -525,7 +525,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root,
  * extra check to make sure the ordered operation list really is empty
  * before we return
  */
-int btrfs_run_ordered_operations(struct btrfs_root *root, int wait)
+void btrfs_run_ordered_operations(struct btrfs_root *root, int wait)
 {
        struct btrfs_inode *btrfs_inode;
        struct inode *inode;
@@ -573,8 +573,6 @@ again:
 
        spin_unlock(&root->fs_info->ordered_extent_lock);
        mutex_unlock(&root->fs_info->ordered_operations_mutex);
-
-       return 0;
 }
 
 /*
@@ -609,7 +607,7 @@ void btrfs_start_ordered_extent(struct inode *inode,
 /*
  * Used to wait on ordered extents across a large range of bytes.
  */
-int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
+void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
 {
        u64 end;
        u64 orig_end;
@@ -664,7 +662,6 @@ again:
                schedule_timeout(1);
                goto again;
        }
-       return 0;
 }
 
 /*
@@ -948,9 +945,8 @@ out:
  * If trans is not null, we'll do a friendly check for a transaction that
  * is already flushing things and force the IO down ourselves.
  */
-int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
-                               struct btrfs_root *root,
-                               struct inode *inode)
+void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root, struct inode *inode)
 {
        u64 last_mod;
 
@@ -961,7 +957,7 @@ int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
         * commit, we can safely return without doing anything
         */
        if (last_mod < root->fs_info->last_trans_committed)
-               return 0;
+               return;
 
        /*
         * the transaction is already committing.  Just start the IO and
@@ -969,7 +965,7 @@ int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
         */
        if (trans && root->fs_info->running_transaction->blocked) {
                btrfs_wait_ordered_range(inode, 0, (u64)-1);
-               return 0;
+               return;
        }
 
        spin_lock(&root->fs_info->ordered_extent_lock);
@@ -978,6 +974,4 @@ int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
                              &root->fs_info->ordered_operations);
        }
        spin_unlock(&root->fs_info->ordered_extent_lock);
-
-       return 0;
 }
index ff1f69aa1883d979e64ba59cbc98fbed487c610e..c355ad4dc1a66962d30557e9bbdc08ca9fc25da8 100644 (file)
@@ -138,8 +138,8 @@ btrfs_ordered_inode_tree_init(struct btrfs_ordered_inode_tree *t)
        t->last = NULL;
 }
 
-int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry);
-int btrfs_remove_ordered_extent(struct inode *inode,
+void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry);
+void btrfs_remove_ordered_extent(struct inode *inode,
                                struct btrfs_ordered_extent *entry);
 int btrfs_dec_test_ordered_pending(struct inode *inode,
                                   struct btrfs_ordered_extent **cached,
@@ -154,14 +154,14 @@ int btrfs_add_ordered_extent_dio(struct inode *inode, u64 file_offset,
 int btrfs_add_ordered_extent_compress(struct inode *inode, u64 file_offset,
                                      u64 start, u64 len, u64 disk_len,
                                      int type, int compress_type);
-int btrfs_add_ordered_sum(struct inode *inode,
-                         struct btrfs_ordered_extent *entry,
-                         struct btrfs_ordered_sum *sum);
+void btrfs_add_ordered_sum(struct inode *inode,
+                          struct btrfs_ordered_extent *entry,
+                          struct btrfs_ordered_sum *sum);
 struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode,
                                                         u64 file_offset);
 void btrfs_start_ordered_extent(struct inode *inode,
                                struct btrfs_ordered_extent *entry, int wait);
-int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len);
+void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len);
 struct btrfs_ordered_extent *
 btrfs_lookup_first_ordered_extent(struct inode * inode, u64 file_offset);
 struct btrfs_ordered_extent *btrfs_lookup_ordered_range(struct inode *inode,
@@ -170,10 +170,10 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_range(struct inode *inode,
 int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
                                struct btrfs_ordered_extent *ordered);
 int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr, u32 *sum);
-int btrfs_run_ordered_operations(struct btrfs_root *root, int wait);
-int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
-                               struct btrfs_root *root,
-                               struct inode *inode);
-int btrfs_wait_ordered_extents(struct btrfs_root *root,
-                              int nocow_only, int delay_iput);
+void btrfs_run_ordered_operations(struct btrfs_root *root, int wait);
+void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root,
+                                struct inode *inode);
+void btrfs_wait_ordered_extents(struct btrfs_root *root,
+                               int nocow_only, int delay_iput);
 #endif
index f8be250963a09a283b8ec4ab04bcb1c3a962427e..24cad1695af74790ecc18182db35a0ed5c8cce7a 100644 (file)
@@ -58,7 +58,7 @@ int btrfs_del_orphan_item(struct btrfs_trans_handle *trans,
        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
        if (ret < 0)
                goto out;
-       if (ret) {
+       if (ret) { /* JDM: Really? */
                ret = -ENOENT;
                goto out;
        }
index 22db04550f6a03ff92580e801b860cc4ed757ebc..dc5d33146fdbb9f4fb3cf899d78a97ca361b6ac8 100644 (file)
@@ -54,7 +54,6 @@
  * than the 2 started one after another.
  */
 
-#define MAX_MIRRORS 2
 #define MAX_IN_FLIGHT 6
 
 struct reada_extctl {
@@ -71,7 +70,7 @@ struct reada_extent {
        struct list_head        extctl;
        struct kref             refcnt;
        spinlock_t              lock;
-       struct reada_zone       *zones[MAX_MIRRORS];
+       struct reada_zone       *zones[BTRFS_MAX_MIRRORS];
        int                     nzones;
        struct btrfs_device     *scheduled_for;
 };
@@ -84,7 +83,8 @@ struct reada_zone {
        spinlock_t              lock;
        int                     locked;
        struct btrfs_device     *device;
-       struct btrfs_device     *devs[MAX_MIRRORS]; /* full list, incl self */
+       struct btrfs_device     *devs[BTRFS_MAX_MIRRORS]; /* full list, incl
+                                                          * self */
        int                     ndevs;
        struct kref             refcnt;
 };
@@ -365,9 +365,9 @@ again:
        if (ret || !bbio || length < blocksize)
                goto error;
 
-       if (bbio->num_stripes > MAX_MIRRORS) {
+       if (bbio->num_stripes > BTRFS_MAX_MIRRORS) {
                printk(KERN_ERR "btrfs readahead: more than %d copies not "
-                               "supported", MAX_MIRRORS);
+                               "supported", BTRFS_MAX_MIRRORS);
                goto error;
        }
 
index 8c1aae2c845d49960fe352c809033f1bdf5ffb74..017281dbb2a71f6a0c51f4f8cc52cf9e651f2fd0 100644 (file)
@@ -326,6 +326,19 @@ static struct rb_node *tree_search(struct rb_root *root, u64 bytenr)
        return NULL;
 }
 
+void backref_tree_panic(struct rb_node *rb_node, int errno,
+                                         u64 bytenr)
+{
+
+       struct btrfs_fs_info *fs_info = NULL;
+       struct backref_node *bnode = rb_entry(rb_node, struct backref_node,
+                                             rb_node);
+       if (bnode->root)
+               fs_info = bnode->root->fs_info;
+       btrfs_panic(fs_info, errno, "Inconsistency in backref cache "
+                   "found at offset %llu\n", (unsigned long long)bytenr);
+}
+
 /*
  * walk up backref nodes until reach node presents tree root
  */
@@ -452,7 +465,8 @@ static void update_backref_node(struct backref_cache *cache,
        rb_erase(&node->rb_node, &cache->rb_root);
        node->bytenr = bytenr;
        rb_node = tree_insert(&cache->rb_root, node->bytenr, &node->rb_node);
-       BUG_ON(rb_node);
+       if (rb_node)
+               backref_tree_panic(rb_node, -EEXIST, bytenr);
 }
 
 /*
@@ -999,7 +1013,8 @@ next:
        if (!cowonly) {
                rb_node = tree_insert(&cache->rb_root, node->bytenr,
                                      &node->rb_node);
-               BUG_ON(rb_node);
+               if (rb_node)
+                       backref_tree_panic(rb_node, -EEXIST, node->bytenr);
                list_add_tail(&node->lower, &cache->leaves);
        }
 
@@ -1034,7 +1049,9 @@ next:
                if (!cowonly) {
                        rb_node = tree_insert(&cache->rb_root, upper->bytenr,
                                              &upper->rb_node);
-                       BUG_ON(rb_node);
+                       if (rb_node)
+                               backref_tree_panic(rb_node, -EEXIST,
+                                                  upper->bytenr);
                }
 
                list_add_tail(&edge->list[UPPER], &upper->lower);
@@ -1180,7 +1197,8 @@ static int clone_backref_node(struct btrfs_trans_handle *trans,
 
        rb_node = tree_insert(&cache->rb_root, new_node->bytenr,
                              &new_node->rb_node);
-       BUG_ON(rb_node);
+       if (rb_node)
+               backref_tree_panic(rb_node, -EEXIST, new_node->bytenr);
 
        if (!new_node->lowest) {
                list_for_each_entry(new_edge, &new_node->lower, list[UPPER]) {
@@ -1203,14 +1221,15 @@ fail:
 /*
  * helper to add 'address of tree root -> reloc tree' mapping
  */
-static int __add_reloc_root(struct btrfs_root *root)
+static int __must_check __add_reloc_root(struct btrfs_root *root)
 {
        struct rb_node *rb_node;
        struct mapping_node *node;
        struct reloc_control *rc = root->fs_info->reloc_ctl;
 
        node = kmalloc(sizeof(*node), GFP_NOFS);
-       BUG_ON(!node);
+       if (!node)
+               return -ENOMEM;
 
        node->bytenr = root->node->start;
        node->data = root;
@@ -1219,7 +1238,12 @@ static int __add_reloc_root(struct btrfs_root *root)
        rb_node = tree_insert(&rc->reloc_root_tree.rb_root,
                              node->bytenr, &node->rb_node);
        spin_unlock(&rc->reloc_root_tree.lock);
-       BUG_ON(rb_node);
+       if (rb_node) {
+               kfree(node);
+               btrfs_panic(root->fs_info, -EEXIST, "Duplicate root found "
+                           "for start=%llu while inserting into relocation "
+                           "tree\n");
+       }
 
        list_add_tail(&root->root_list, &rc->reloc_roots);
        return 0;
@@ -1252,7 +1276,8 @@ static int __update_reloc_root(struct btrfs_root *root, int del)
                rb_node = tree_insert(&rc->reloc_root_tree.rb_root,
                                      node->bytenr, &node->rb_node);
                spin_unlock(&rc->reloc_root_tree.lock);
-               BUG_ON(rb_node);
+               if (rb_node)
+                       backref_tree_panic(rb_node, -EEXIST, node->bytenr);
        } else {
                list_del_init(&root->root_list);
                kfree(node);
@@ -1334,6 +1359,7 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
        struct btrfs_root *reloc_root;
        struct reloc_control *rc = root->fs_info->reloc_ctl;
        int clear_rsv = 0;
+       int ret;
 
        if (root->reloc_root) {
                reloc_root = root->reloc_root;
@@ -1353,7 +1379,8 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
        if (clear_rsv)
                trans->block_rsv = NULL;
 
-       __add_reloc_root(reloc_root);
+       ret = __add_reloc_root(reloc_root);
+       BUG_ON(ret < 0);
        root->reloc_root = reloc_root;
        return 0;
 }
@@ -1577,15 +1604,14 @@ int replace_file_extents(struct btrfs_trans_handle *trans,
                                WARN_ON(!IS_ALIGNED(end, root->sectorsize));
                                end--;
                                ret = try_lock_extent(&BTRFS_I(inode)->io_tree,
-                                                     key.offset, end,
-                                                     GFP_NOFS);
+                                                     key.offset, end);
                                if (!ret)
                                        continue;
 
                                btrfs_drop_extent_cache(inode, key.offset, end,
                                                        1);
                                unlock_extent(&BTRFS_I(inode)->io_tree,
-                                             key.offset, end, GFP_NOFS);
+                                             key.offset, end);
                        }
                }
 
@@ -1956,9 +1982,9 @@ static int invalidate_extent_cache(struct btrfs_root *root,
                }
 
                /* the lock_extent waits for readpage to complete */
-               lock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
+               lock_extent(&BTRFS_I(inode)->io_tree, start, end);
                btrfs_drop_extent_cache(inode, start, end, 1);
-               unlock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
+               unlock_extent(&BTRFS_I(inode)->io_tree, start, end);
        }
        return 0;
 }
@@ -2246,7 +2272,8 @@ again:
                } else {
                        list_del_init(&reloc_root->root_list);
                }
-               btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1);
+               ret = btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1);
+               BUG_ON(ret < 0);
        }
 
        if (found) {
@@ -2862,12 +2889,12 @@ int prealloc_file_extent_cluster(struct inode *inode,
                else
                        end = cluster->end - offset;
 
-               lock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
+               lock_extent(&BTRFS_I(inode)->io_tree, start, end);
                num_bytes = end + 1 - start;
                ret = btrfs_prealloc_file_range(inode, 0, start,
                                                num_bytes, num_bytes,
                                                end + 1, &alloc_hint);
-               unlock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
+               unlock_extent(&BTRFS_I(inode)->io_tree, start, end);
                if (ret)
                        break;
                nr++;
@@ -2899,7 +2926,7 @@ int setup_extent_mapping(struct inode *inode, u64 start, u64 end,
        em->bdev = root->fs_info->fs_devices->latest_bdev;
        set_bit(EXTENT_FLAG_PINNED, &em->flags);
 
-       lock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
+       lock_extent(&BTRFS_I(inode)->io_tree, start, end);
        while (1) {
                write_lock(&em_tree->lock);
                ret = add_extent_mapping(em_tree, em);
@@ -2910,7 +2937,7 @@ int setup_extent_mapping(struct inode *inode, u64 start, u64 end,
                }
                btrfs_drop_extent_cache(inode, start, end, 0);
        }
-       unlock_extent(&BTRFS_I(inode)->io_tree, start, end, GFP_NOFS);
+       unlock_extent(&BTRFS_I(inode)->io_tree, start, end);
        return ret;
 }
 
@@ -2990,8 +3017,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
                page_start = (u64)page->index << PAGE_CACHE_SHIFT;
                page_end = page_start + PAGE_CACHE_SIZE - 1;
 
-               lock_extent(&BTRFS_I(inode)->io_tree,
-                           page_start, page_end, GFP_NOFS);
+               lock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end);
 
                set_page_extent_mapped(page);
 
@@ -3007,7 +3033,7 @@ static int relocate_file_extent_cluster(struct inode *inode,
                set_page_dirty(page);
 
                unlock_extent(&BTRFS_I(inode)->io_tree,
-                             page_start, page_end, GFP_NOFS);
+                             page_start, page_end);
                unlock_page(page);
                page_cache_release(page);
 
@@ -3154,7 +3180,8 @@ static int add_tree_block(struct reloc_control *rc,
        block->key_ready = 0;
 
        rb_node = tree_insert(blocks, block->bytenr, &block->rb_node);
-       BUG_ON(rb_node);
+       if (rb_node)
+               backref_tree_panic(rb_node, -EEXIST, block->bytenr);
 
        return 0;
 }
@@ -3426,7 +3453,9 @@ static int find_data_references(struct reloc_control *rc,
                        block->key_ready = 1;
                        rb_node = tree_insert(blocks, block->bytenr,
                                              &block->rb_node);
-                       BUG_ON(rb_node);
+                       if (rb_node)
+                               backref_tree_panic(rb_node, -EEXIST,
+                                                  block->bytenr);
                }
                if (counted)
                        added = 1;
@@ -4073,10 +4102,11 @@ out:
 static noinline_for_stack int mark_garbage_root(struct btrfs_root *root)
 {
        struct btrfs_trans_handle *trans;
-       int ret;
+       int ret, err;
 
        trans = btrfs_start_transaction(root->fs_info->tree_root, 0);
-       BUG_ON(IS_ERR(trans));
+       if (IS_ERR(trans))
+               return PTR_ERR(trans);
 
        memset(&root->root_item.drop_progress, 0,
                sizeof(root->root_item.drop_progress));
@@ -4084,11 +4114,11 @@ static noinline_for_stack int mark_garbage_root(struct btrfs_root *root)
        btrfs_set_root_refs(&root->root_item, 0);
        ret = btrfs_update_root(trans, root->fs_info->tree_root,
                                &root->root_key, &root->root_item);
-       BUG_ON(ret);
 
-       ret = btrfs_end_transaction(trans, root->fs_info->tree_root);
-       BUG_ON(ret);
-       return 0;
+       err = btrfs_end_transaction(trans, root->fs_info->tree_root);
+       if (err)
+               return err;
+       return ret;
 }
 
 /*
@@ -4156,7 +4186,11 @@ int btrfs_recover_relocation(struct btrfs_root *root)
                                        err = ret;
                                        goto out;
                                }
-                               mark_garbage_root(reloc_root);
+                               ret = mark_garbage_root(reloc_root);
+                               if (ret < 0) {
+                                       err = ret;
+                                       goto out;
+                               }
                        }
                }
 
@@ -4202,13 +4236,19 @@ int btrfs_recover_relocation(struct btrfs_root *root)
 
                fs_root = read_fs_root(root->fs_info,
                                       reloc_root->root_key.offset);
-               BUG_ON(IS_ERR(fs_root));
+               if (IS_ERR(fs_root)) {
+                       err = PTR_ERR(fs_root);
+                       goto out_free;
+               }
 
-               __add_reloc_root(reloc_root);
+               err = __add_reloc_root(reloc_root);
+               BUG_ON(err < 0); /* -ENOMEM or logic error */
                fs_root->reloc_root = reloc_root;
        }
 
-       btrfs_commit_transaction(trans, rc->extent_root);
+       err = btrfs_commit_transaction(trans, rc->extent_root);
+       if (err)
+               goto out_free;
 
        merge_reloc_roots(rc);
 
@@ -4218,7 +4258,7 @@ int btrfs_recover_relocation(struct btrfs_root *root)
        if (IS_ERR(trans))
                err = PTR_ERR(trans);
        else
-               btrfs_commit_transaction(trans, rc->extent_root);
+               err = btrfs_commit_transaction(trans, rc->extent_root);
 out_free:
        kfree(rc);
 out:
@@ -4267,6 +4307,8 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
        disk_bytenr = file_pos + BTRFS_I(inode)->index_cnt;
        ret = btrfs_lookup_csums_range(root->fs_info->csum_root, disk_bytenr,
                                       disk_bytenr + len - 1, &list, 0);
+       if (ret)
+               goto out;
 
        while (!list_empty(&list)) {
                sums = list_entry(list.next, struct btrfs_ordered_sum, list);
@@ -4284,6 +4326,7 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
 
                btrfs_add_ordered_sum(inode, ordered, sums);
        }
+out:
        btrfs_put_ordered_extent(ordered);
        return ret;
 }
@@ -4380,7 +4423,7 @@ void btrfs_reloc_pre_snapshot(struct btrfs_trans_handle *trans,
  * called after snapshot is created. migrate block reservation
  * and create reloc root for the newly created snapshot
  */
-void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
+int btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
                               struct btrfs_pending_snapshot *pending)
 {
        struct btrfs_root *root = pending->root;
@@ -4390,7 +4433,7 @@ void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
        int ret;
 
        if (!root->reloc_root)
-               return;
+               return 0;
 
        rc = root->fs_info->reloc_ctl;
        rc->merging_rsv_size += rc->nodes_relocated;
@@ -4399,18 +4442,21 @@ void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
                ret = btrfs_block_rsv_migrate(&pending->block_rsv,
                                              rc->block_rsv,
                                              rc->nodes_relocated);
-               BUG_ON(ret);
+               if (ret)
+                       return ret;
        }
 
        new_root = pending->snap;
        reloc_root = create_reloc_root(trans, root->reloc_root,
                                       new_root->root_key.objectid);
+       if (IS_ERR(reloc_root))
+               return PTR_ERR(reloc_root);
 
-       __add_reloc_root(reloc_root);
+       ret = __add_reloc_root(reloc_root);
+       BUG_ON(ret < 0);
        new_root->reloc_root = reloc_root;
 
-       if (rc->create_reloc_tree) {
+       if (rc->create_reloc_tree)
                ret = clone_backref_node(trans, rc, root, reloc_root);
-               BUG_ON(ret);
-       }
+       return ret;
 }
index f4099904565a508807f9c43458a1cdab6200cc24..24fb8ce4e071091ace14af9db4f784eb6f0ea3d9 100644 (file)
@@ -93,10 +93,14 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
        unsigned long ptr;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
+
        ret = btrfs_search_slot(trans, root, key, path, 0, 1);
-       if (ret < 0)
+       if (ret < 0) {
+               btrfs_abort_transaction(trans, root, ret);
                goto out;
+       }
 
        if (ret != 0) {
                btrfs_print_leaf(root, path->nodes[0]);
@@ -116,13 +120,10 @@ out:
        return ret;
 }
 
-int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root
-                     *root, struct btrfs_key *key, struct btrfs_root_item
-                     *item)
+int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+                     struct btrfs_key *key, struct btrfs_root_item *item)
 {
-       int ret;
-       ret = btrfs_insert_item(trans, root, key, item, sizeof(*item));
-       return ret;
+       return btrfs_insert_item(trans, root, key, item, sizeof(*item));
 }
 
 /*
@@ -384,6 +385,8 @@ int btrfs_find_root_ref(struct btrfs_root *tree_root,
  *
  * For a back ref the root_id is the id of the subvol or snapshot and
  * ref_id is the id of the tree referencing it.
+ *
+ * Will return 0, -ENOMEM, or anything from the CoW path
  */
 int btrfs_add_root_ref(struct btrfs_trans_handle *trans,
                       struct btrfs_root *tree_root,
@@ -407,7 +410,11 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans,
 again:
        ret = btrfs_insert_empty_item(trans, tree_root, path, &key,
                                      sizeof(*ref) + name_len);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, tree_root, ret);
+               btrfs_free_path(path);
+               return ret;
+       }
 
        leaf = path->nodes[0];
        ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref);
index 390e7102b0ffacb6167f6ab4445f4acaaa8db7d5..90acc82046c3ab80b459ff076ced3e97ea4b0e3c 100644 (file)
  * Future enhancements:
  *  - In case an unrepairable extent is encountered, track which files are
  *    affected and report them
- *  - In case of a read error on files with nodatasum, map the file and read
- *    the extent to trigger a writeback of the good copy
  *  - track and record media errors, throw out bad devices
  *  - add a mode to also read unallocated space
  */
 
-struct scrub_bio;
-struct scrub_page;
+struct scrub_block;
 struct scrub_dev;
-static void scrub_bio_end_io(struct bio *bio, int err);
-static void scrub_checksum(struct btrfs_work *work);
-static int scrub_checksum_data(struct scrub_dev *sdev,
-                              struct scrub_page *spag, void *buffer);
-static int scrub_checksum_tree_block(struct scrub_dev *sdev,
-                                    struct scrub_page *spag, u64 logical,
-                                    void *buffer);
-static int scrub_checksum_super(struct scrub_bio *sbio, void *buffer);
-static int scrub_fixup_check(struct scrub_bio *sbio, int ix);
-static void scrub_fixup_end_io(struct bio *bio, int err);
-static int scrub_fixup_io(int rw, struct block_device *bdev, sector_t sector,
-                         struct page *page);
-static void scrub_fixup(struct scrub_bio *sbio, int ix);
 
 #define SCRUB_PAGES_PER_BIO    16      /* 64k per bio */
 #define SCRUB_BIOS_PER_DEV     16      /* 1 MB per device in flight */
+#define SCRUB_MAX_PAGES_PER_BLOCK      16      /* 64k per node/leaf/sector */
 
 struct scrub_page {
+       struct scrub_block      *sblock;
+       struct page             *page;
+       struct block_device     *bdev;
        u64                     flags;  /* extent flags */
        u64                     generation;
-       int                     mirror_num;
-       int                     have_csum;
+       u64                     logical;
+       u64                     physical;
+       struct {
+               unsigned int    mirror_num:8;
+               unsigned int    have_csum:1;
+               unsigned int    io_error:1;
+       };
        u8                      csum[BTRFS_CSUM_SIZE];
 };
 
@@ -77,12 +70,25 @@ struct scrub_bio {
        int                     err;
        u64                     logical;
        u64                     physical;
-       struct scrub_page       spag[SCRUB_PAGES_PER_BIO];
-       u64                     count;
+       struct scrub_page       *pagev[SCRUB_PAGES_PER_BIO];
+       int                     page_count;
        int                     next_free;
        struct btrfs_work       work;
 };
 
+struct scrub_block {
+       struct scrub_page       pagev[SCRUB_MAX_PAGES_PER_BLOCK];
+       int                     page_count;
+       atomic_t                outstanding_pages;
+       atomic_t                ref_count; /* free mem on transition to zero */
+       struct scrub_dev        *sdev;
+       struct {
+               unsigned int    header_error:1;
+               unsigned int    checksum_error:1;
+               unsigned int    no_io_error_seen:1;
+       };
+};
+
 struct scrub_dev {
        struct scrub_bio        *bios[SCRUB_BIOS_PER_DEV];
        struct btrfs_device     *dev;
@@ -96,6 +102,10 @@ struct scrub_dev {
        struct list_head        csum_list;
        atomic_t                cancel_req;
        int                     readonly;
+       int                     pages_per_bio; /* <= SCRUB_PAGES_PER_BIO */
+       u32                     sectorsize;
+       u32                     nodesize;
+       u32                     leafsize;
        /*
         * statistics
         */
@@ -124,6 +134,43 @@ struct scrub_warning {
        int                     scratch_bufsize;
 };
 
+
+static int scrub_handle_errored_block(struct scrub_block *sblock_to_check);
+static int scrub_setup_recheck_block(struct scrub_dev *sdev,
+                                    struct btrfs_mapping_tree *map_tree,
+                                    u64 length, u64 logical,
+                                    struct scrub_block *sblock);
+static int scrub_recheck_block(struct btrfs_fs_info *fs_info,
+                              struct scrub_block *sblock, int is_metadata,
+                              int have_csum, u8 *csum, u64 generation,
+                              u16 csum_size);
+static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
+                                        struct scrub_block *sblock,
+                                        int is_metadata, int have_csum,
+                                        const u8 *csum, u64 generation,
+                                        u16 csum_size);
+static void scrub_complete_bio_end_io(struct bio *bio, int err);
+static int scrub_repair_block_from_good_copy(struct scrub_block *sblock_bad,
+                                            struct scrub_block *sblock_good,
+                                            int force_write);
+static int scrub_repair_page_from_good_copy(struct scrub_block *sblock_bad,
+                                           struct scrub_block *sblock_good,
+                                           int page_num, int force_write);
+static int scrub_checksum_data(struct scrub_block *sblock);
+static int scrub_checksum_tree_block(struct scrub_block *sblock);
+static int scrub_checksum_super(struct scrub_block *sblock);
+static void scrub_block_get(struct scrub_block *sblock);
+static void scrub_block_put(struct scrub_block *sblock);
+static int scrub_add_page_to_bio(struct scrub_dev *sdev,
+                                struct scrub_page *spage);
+static int scrub_pages(struct scrub_dev *sdev, u64 logical, u64 len,
+                      u64 physical, u64 flags, u64 gen, int mirror_num,
+                      u8 *csum, int force);
+static void scrub_bio_end_io(struct bio *bio, int err);
+static void scrub_bio_end_io_worker(struct btrfs_work *work);
+static void scrub_block_complete(struct scrub_block *sblock);
+
+
 static void scrub_free_csums(struct scrub_dev *sdev)
 {
        while (!list_empty(&sdev->csum_list)) {
@@ -135,23 +182,6 @@ static void scrub_free_csums(struct scrub_dev *sdev)
        }
 }
 
-static void scrub_free_bio(struct bio *bio)
-{
-       int i;
-       struct page *last_page = NULL;
-
-       if (!bio)
-               return;
-
-       for (i = 0; i < bio->bi_vcnt; ++i) {
-               if (bio->bi_io_vec[i].bv_page == last_page)
-                       continue;
-               last_page = bio->bi_io_vec[i].bv_page;
-               __free_page(last_page);
-       }
-       bio_put(bio);
-}
-
 static noinline_for_stack void scrub_free_dev(struct scrub_dev *sdev)
 {
        int i;
@@ -159,13 +189,23 @@ static noinline_for_stack void scrub_free_dev(struct scrub_dev *sdev)
        if (!sdev)
                return;
 
+       /* this can happen when scrub is cancelled */
+       if (sdev->curr != -1) {
+               struct scrub_bio *sbio = sdev->bios[sdev->curr];
+
+               for (i = 0; i < sbio->page_count; i++) {
+                       BUG_ON(!sbio->pagev[i]);
+                       BUG_ON(!sbio->pagev[i]->page);
+                       scrub_block_put(sbio->pagev[i]->sblock);
+               }
+               bio_put(sbio->bio);
+       }
+
        for (i = 0; i < SCRUB_BIOS_PER_DEV; ++i) {
                struct scrub_bio *sbio = sdev->bios[i];
 
                if (!sbio)
                        break;
-
-               scrub_free_bio(sbio->bio);
                kfree(sbio);
        }
 
@@ -179,11 +219,16 @@ struct scrub_dev *scrub_setup_dev(struct btrfs_device *dev)
        struct scrub_dev *sdev;
        int             i;
        struct btrfs_fs_info *fs_info = dev->dev_root->fs_info;
+       int pages_per_bio;
 
+       pages_per_bio = min_t(int, SCRUB_PAGES_PER_BIO,
+                             bio_get_nr_vecs(dev->bdev));
        sdev = kzalloc(sizeof(*sdev), GFP_NOFS);
        if (!sdev)
                goto nomem;
        sdev->dev = dev;
+       sdev->pages_per_bio = pages_per_bio;
+       sdev->curr = -1;
        for (i = 0; i < SCRUB_BIOS_PER_DEV; ++i) {
                struct scrub_bio *sbio;
 
@@ -194,8 +239,8 @@ struct scrub_dev *scrub_setup_dev(struct btrfs_device *dev)
 
                sbio->index = i;
                sbio->sdev = sdev;
-               sbio->count = 0;
-               sbio->work.func = scrub_checksum;
+               sbio->page_count = 0;
+               sbio->work.func = scrub_bio_end_io_worker;
 
                if (i != SCRUB_BIOS_PER_DEV-1)
                        sdev->bios[i]->next_free = i + 1;
@@ -203,7 +248,9 @@ struct scrub_dev *scrub_setup_dev(struct btrfs_device *dev)
                        sdev->bios[i]->next_free = -1;
        }
        sdev->first_free = 0;
-       sdev->curr = -1;
+       sdev->nodesize = dev->dev_root->nodesize;
+       sdev->leafsize = dev->dev_root->leafsize;
+       sdev->sectorsize = dev->dev_root->sectorsize;
        atomic_set(&sdev->in_flight, 0);
        atomic_set(&sdev->fixup_cnt, 0);
        atomic_set(&sdev->cancel_req, 0);
@@ -294,10 +341,9 @@ err:
        return 0;
 }
 
-static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio,
-                               int ix)
+static void scrub_print_warning(const char *errstr, struct scrub_block *sblock)
 {
-       struct btrfs_device *dev = sbio->sdev->dev;
+       struct btrfs_device *dev = sblock->sdev->dev;
        struct btrfs_fs_info *fs_info = dev->dev_root->fs_info;
        struct btrfs_path *path;
        struct btrfs_key found_key;
@@ -316,8 +362,9 @@ static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio,
 
        swarn.scratch_buf = kmalloc(bufsize, GFP_NOFS);
        swarn.msg_buf = kmalloc(bufsize, GFP_NOFS);
-       swarn.sector = (sbio->physical + ix * PAGE_SIZE) >> 9;
-       swarn.logical = sbio->logical + ix * PAGE_SIZE;
+       BUG_ON(sblock->page_count < 1);
+       swarn.sector = (sblock->pagev[0].physical) >> 9;
+       swarn.logical = sblock->pagev[0].logical;
        swarn.errstr = errstr;
        swarn.dev = dev;
        swarn.msg_bufsize = bufsize;
@@ -342,7 +389,8 @@ static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio,
                do {
                        ret = tree_backref_for_extent(&ptr, eb, ei, item_size,
                                                        &ref_root, &ref_level);
-                       printk(KERN_WARNING "%s at logical %llu on dev %s, "
+                       printk(KERN_WARNING
+                               "btrfs: %s at logical %llu on dev %s, "
                                "sector %llu: metadata %s (level %d) in tree "
                                "%llu\n", errstr, swarn.logical, dev->name,
                                (unsigned long long)swarn.sector,
@@ -352,8 +400,8 @@ static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio,
                } while (ret != 1);
        } else {
                swarn.path = path;
-               iterate_extent_inodes(fs_info, path, found_key.objectid,
-                                       extent_item_pos,
+               iterate_extent_inodes(fs_info, found_key.objectid,
+                                       extent_item_pos, 1,
                                        scrub_print_warning_inode, &swarn);
        }
 
@@ -531,9 +579,9 @@ out:
                spin_lock(&sdev->stat_lock);
                ++sdev->stat.uncorrectable_errors;
                spin_unlock(&sdev->stat_lock);
-               printk_ratelimited(KERN_ERR "btrfs: unable to fixup "
-                                       "(nodatasum) error at logical %llu\n",
-                                       fixup->logical);
+               printk_ratelimited(KERN_ERR
+                       "btrfs: unable to fixup (nodatasum) error at logical %llu on dev %s\n",
+                       (unsigned long long)fixup->logical, sdev->dev->name);
        }
 
        btrfs_free_path(path);
@@ -550,91 +598,168 @@ out:
 }
 
 /*
- * scrub_recheck_error gets called when either verification of the page
- * failed or the bio failed to read, e.g. with EIO. In the latter case,
- * recheck_error gets called for every page in the bio, even though only
- * one may be bad
+ * scrub_handle_errored_block gets called when either verification of the
+ * pages failed or the bio failed to read, e.g. with EIO. In the latter
+ * case, this function handles all pages in the bio, even though only one
+ * may be bad.
+ * The goal of this function is to repair the errored block by using the
+ * contents of one of the mirrors.
  */
-static int scrub_recheck_error(struct scrub_bio *sbio, int ix)
+static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
 {
-       struct scrub_dev *sdev = sbio->sdev;
-       u64 sector = (sbio->physical + ix * PAGE_SIZE) >> 9;
+       struct scrub_dev *sdev = sblock_to_check->sdev;
+       struct btrfs_fs_info *fs_info;
+       u64 length;
+       u64 logical;
+       u64 generation;
+       unsigned int failed_mirror_index;
+       unsigned int is_metadata;
+       unsigned int have_csum;
+       u8 *csum;
+       struct scrub_block *sblocks_for_recheck; /* holds one for each mirror */
+       struct scrub_block *sblock_bad;
+       int ret;
+       int mirror_index;
+       int page_num;
+       int success;
        static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
-                                       DEFAULT_RATELIMIT_BURST);
+                                     DEFAULT_RATELIMIT_BURST);
+
+       BUG_ON(sblock_to_check->page_count < 1);
+       fs_info = sdev->dev->dev_root->fs_info;
+       length = sblock_to_check->page_count * PAGE_SIZE;
+       logical = sblock_to_check->pagev[0].logical;
+       generation = sblock_to_check->pagev[0].generation;
+       BUG_ON(sblock_to_check->pagev[0].mirror_num < 1);
+       failed_mirror_index = sblock_to_check->pagev[0].mirror_num - 1;
+       is_metadata = !(sblock_to_check->pagev[0].flags &
+                       BTRFS_EXTENT_FLAG_DATA);
+       have_csum = sblock_to_check->pagev[0].have_csum;
+       csum = sblock_to_check->pagev[0].csum;
 
-       if (sbio->err) {
-               if (scrub_fixup_io(READ, sbio->sdev->dev->bdev, sector,
-                                  sbio->bio->bi_io_vec[ix].bv_page) == 0) {
-                       if (scrub_fixup_check(sbio, ix) == 0)
-                               return 0;
-               }
-               if (__ratelimit(&_rs))
-                       scrub_print_warning("i/o error", sbio, ix);
-       } else {
-               if (__ratelimit(&_rs))
-                       scrub_print_warning("checksum error", sbio, ix);
+       /*
+        * read all mirrors one after the other. This includes to
+        * re-read the extent or metadata block that failed (that was
+        * the cause that this fixup code is called) another time,
+        * page by page this time in order to know which pages
+        * caused I/O errors and which ones are good (for all mirrors).
+        * It is the goal to handle the situation when more than one
+        * mirror contains I/O errors, but the errors do not
+        * overlap, i.e. the data can be repaired by selecting the
+        * pages from those mirrors without I/O error on the
+        * particular pages. One example (with blocks >= 2 * PAGE_SIZE)
+        * would be that mirror #1 has an I/O error on the first page,
+        * the second page is good, and mirror #2 has an I/O error on
+        * the second page, but the first page is good.
+        * Then the first page of the first mirror can be repaired by
+        * taking the first page of the second mirror, and the
+        * second page of the second mirror can be repaired by
+        * copying the contents of the 2nd page of the 1st mirror.
+        * One more note: if the pages of one mirror contain I/O
+        * errors, the checksum cannot be verified. In order to get
+        * the best data for repairing, the first attempt is to find
+        * a mirror without I/O errors and with a validated checksum.
+        * Only if this is not possible, the pages are picked from
+        * mirrors with I/O errors without considering the checksum.
+        * If the latter is the case, at the end, the checksum of the
+        * repaired area is verified in order to correctly maintain
+        * the statistics.
+        */
+
+       sblocks_for_recheck = kzalloc(BTRFS_MAX_MIRRORS *
+                                    sizeof(*sblocks_for_recheck),
+                                    GFP_NOFS);
+       if (!sblocks_for_recheck) {
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.malloc_errors++;
+               sdev->stat.read_errors++;
+               sdev->stat.uncorrectable_errors++;
+               spin_unlock(&sdev->stat_lock);
+               goto out;
        }
 
-       spin_lock(&sdev->stat_lock);
-       ++sdev->stat.read_errors;
-       spin_unlock(&sdev->stat_lock);
+       /* setup the context, map the logical blocks and alloc the pages */
+       ret = scrub_setup_recheck_block(sdev, &fs_info->mapping_tree, length,
+                                       logical, sblocks_for_recheck);
+       if (ret) {
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.read_errors++;
+               sdev->stat.uncorrectable_errors++;
+               spin_unlock(&sdev->stat_lock);
+               goto out;
+       }
+       BUG_ON(failed_mirror_index >= BTRFS_MAX_MIRRORS);
+       sblock_bad = sblocks_for_recheck + failed_mirror_index;
 
-       scrub_fixup(sbio, ix);
-       return 1;
-}
+       /* build and submit the bios for the failed mirror, check checksums */
+       ret = scrub_recheck_block(fs_info, sblock_bad, is_metadata, have_csum,
+                                 csum, generation, sdev->csum_size);
+       if (ret) {
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.read_errors++;
+               sdev->stat.uncorrectable_errors++;
+               spin_unlock(&sdev->stat_lock);
+               goto out;
+       }
 
-static int scrub_fixup_check(struct scrub_bio *sbio, int ix)
-{
-       int ret = 1;
-       struct page *page;
-       void *buffer;
-       u64 flags = sbio->spag[ix].flags;
+       if (!sblock_bad->header_error && !sblock_bad->checksum_error &&
+           sblock_bad->no_io_error_seen) {
+               /*
+                * the error disappeared after reading page by page, or
+                * the area was part of a huge bio and other parts of the
+                * bio caused I/O errors, or the block layer merged several
+                * read requests into one and the error is caused by a
+                * different bio (usually one of the two latter cases is
+                * the cause)
+                */
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.unverified_errors++;
+               spin_unlock(&sdev->stat_lock);
 
-       page = sbio->bio->bi_io_vec[ix].bv_page;
-       buffer = kmap_atomic(page);
-       if (flags & BTRFS_EXTENT_FLAG_DATA) {
-               ret = scrub_checksum_data(sbio->sdev,
-                                         sbio->spag + ix, buffer);
-       } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
-               ret = scrub_checksum_tree_block(sbio->sdev,
-                                               sbio->spag + ix,
-                                               sbio->logical + ix * PAGE_SIZE,
-                                               buffer);
-       } else {
-               WARN_ON(1);
+               goto out;
        }
-       kunmap_atomic(buffer);
 
-       return ret;
-}
+       if (!sblock_bad->no_io_error_seen) {
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.read_errors++;
+               spin_unlock(&sdev->stat_lock);
+               if (__ratelimit(&_rs))
+                       scrub_print_warning("i/o error", sblock_to_check);
+       } else if (sblock_bad->checksum_error) {
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.csum_errors++;
+               spin_unlock(&sdev->stat_lock);
+               if (__ratelimit(&_rs))
+                       scrub_print_warning("checksum error", sblock_to_check);
+       } else if (sblock_bad->header_error) {
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.verify_errors++;
+               spin_unlock(&sdev->stat_lock);
+               if (__ratelimit(&_rs))
+                       scrub_print_warning("checksum/header error",
+                                           sblock_to_check);
+       }
 
-static void scrub_fixup_end_io(struct bio *bio, int err)
-{
-       complete((struct completion *)bio->bi_private);
-}
+       if (sdev->readonly)
+               goto did_not_correct_error;
 
-static void scrub_fixup(struct scrub_bio *sbio, int ix)
-{
-       struct scrub_dev *sdev = sbio->sdev;
-       struct btrfs_fs_info *fs_info = sdev->dev->dev_root->fs_info;
-       struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
-       struct btrfs_bio *bbio = NULL;
-       struct scrub_fixup_nodatasum *fixup;
-       u64 logical = sbio->logical + ix * PAGE_SIZE;
-       u64 length;
-       int i;
-       int ret;
-       DECLARE_COMPLETION_ONSTACK(complete);
-
-       if ((sbio->spag[ix].flags & BTRFS_EXTENT_FLAG_DATA) &&
-           (sbio->spag[ix].have_csum == 0)) {
-               fixup = kzalloc(sizeof(*fixup), GFP_NOFS);
-               if (!fixup)
-                       goto uncorrectable;
-               fixup->sdev = sdev;
-               fixup->logical = logical;
-               fixup->root = fs_info->extent_root;
-               fixup->mirror_num = sbio->spag[ix].mirror_num;
+       if (!is_metadata && !have_csum) {
+               struct scrub_fixup_nodatasum *fixup_nodatasum;
+
+               /*
+                * !is_metadata and !have_csum, this means that the data
+                * might not be COW'ed, that it might be modified
+                * concurrently. The general strategy to work on the
+                * commit root does not help in the case when COW is not
+                * used.
+                */
+               fixup_nodatasum = kzalloc(sizeof(*fixup_nodatasum), GFP_NOFS);
+               if (!fixup_nodatasum)
+                       goto did_not_correct_error;
+               fixup_nodatasum->sdev = sdev;
+               fixup_nodatasum->logical = logical;
+               fixup_nodatasum->root = fs_info->extent_root;
+               fixup_nodatasum->mirror_num = failed_mirror_index + 1;
                /*
                 * increment scrubs_running to prevent cancel requests from
                 * completing as long as a fixup worker is running. we must also
@@ -649,235 +774,528 @@ static void scrub_fixup(struct scrub_bio *sbio, int ix)
                atomic_inc(&fs_info->scrubs_paused);
                mutex_unlock(&fs_info->scrub_lock);
                atomic_inc(&sdev->fixup_cnt);
-               fixup->work.func = scrub_fixup_nodatasum;
-               btrfs_queue_worker(&fs_info->scrub_workers, &fixup->work);
-               return;
+               fixup_nodatasum->work.func = scrub_fixup_nodatasum;
+               btrfs_queue_worker(&fs_info->scrub_workers,
+                                  &fixup_nodatasum->work);
+               goto out;
        }
 
-       length = PAGE_SIZE;
-       ret = btrfs_map_block(map_tree, REQ_WRITE, logical, &length,
-                             &bbio, 0);
-       if (ret || !bbio || length < PAGE_SIZE) {
-               printk(KERN_ERR
-                      "scrub_fixup: btrfs_map_block failed us for %llu\n",
-                      (unsigned long long)logical);
-               WARN_ON(1);
-               kfree(bbio);
-               return;
+       /*
+        * now build and submit the bios for the other mirrors, check
+        * checksums
+        */
+       for (mirror_index = 0;
+            mirror_index < BTRFS_MAX_MIRRORS &&
+            sblocks_for_recheck[mirror_index].page_count > 0;
+            mirror_index++) {
+               if (mirror_index == failed_mirror_index)
+                       continue;
+
+               /* build and submit the bios, check checksums */
+               ret = scrub_recheck_block(fs_info,
+                                         sblocks_for_recheck + mirror_index,
+                                         is_metadata, have_csum, csum,
+                                         generation, sdev->csum_size);
+               if (ret)
+                       goto did_not_correct_error;
        }
 
-       if (bbio->num_stripes == 1)
-               /* there aren't any replicas */
-               goto uncorrectable;
+       /*
+        * first try to pick the mirror which is completely without I/O
+        * errors and also does not have a checksum error.
+        * If one is found, and if a checksum is present, the full block
+        * that is known to contain an error is rewritten. Afterwards
+        * the block is known to be corrected.
+        * If a mirror is found which is completely correct, and no
+        * checksum is present, only those pages are rewritten that had
+        * an I/O error in the block to be repaired, since it cannot be
+        * determined, which copy of the other pages is better (and it
+        * could happen otherwise that a correct page would be
+        * overwritten by a bad one).
+        */
+       for (mirror_index = 0;
+            mirror_index < BTRFS_MAX_MIRRORS &&
+            sblocks_for_recheck[mirror_index].page_count > 0;
+            mirror_index++) {
+               struct scrub_block *sblock_other = sblocks_for_recheck +
+                                                  mirror_index;
+
+               if (!sblock_other->header_error &&
+                   !sblock_other->checksum_error &&
+                   sblock_other->no_io_error_seen) {
+                       int force_write = is_metadata || have_csum;
+
+                       ret = scrub_repair_block_from_good_copy(sblock_bad,
+                                                               sblock_other,
+                                                               force_write);
+                       if (0 == ret)
+                               goto corrected_error;
+               }
+       }
 
        /*
-        * first find a good copy
+        * in case of I/O errors in the area that is supposed to be
+        * repaired, continue by picking good copies of those pages.
+        * Select the good pages from mirrors to rewrite bad pages from
+        * the area to fix. Afterwards verify the checksum of the block
+        * that is supposed to be repaired. This verification step is
+        * only done for the purpose of statistic counting and for the
+        * final scrub report, whether errors remain.
+        * A perfect algorithm could make use of the checksum and try
+        * all possible combinations of pages from the different mirrors
+        * until the checksum verification succeeds. For example, when
+        * the 2nd page of mirror #1 faces I/O errors, and the 2nd page
+        * of mirror #2 is readable but the final checksum test fails,
+        * then the 2nd page of mirror #3 could be tried, whether now
+        * the final checksum succeedes. But this would be a rare
+        * exception and is therefore not implemented. At least it is
+        * avoided that the good copy is overwritten.
+        * A more useful improvement would be to pick the sectors
+        * without I/O error based on sector sizes (512 bytes on legacy
+        * disks) instead of on PAGE_SIZE. Then maybe 512 byte of one
+        * mirror could be repaired by taking 512 byte of a different
+        * mirror, even if other 512 byte sectors in the same PAGE_SIZE
+        * area are unreadable.
         */
-       for (i = 0; i < bbio->num_stripes; ++i) {
-               if (i + 1 == sbio->spag[ix].mirror_num)
-                       continue;
 
-               if (scrub_fixup_io(READ, bbio->stripes[i].dev->bdev,
-                                  bbio->stripes[i].physical >> 9,
-                                  sbio->bio->bi_io_vec[ix].bv_page)) {
-                       /* I/O-error, this is not a good copy */
+       /* can only fix I/O errors from here on */
+       if (sblock_bad->no_io_error_seen)
+               goto did_not_correct_error;
+
+       success = 1;
+       for (page_num = 0; page_num < sblock_bad->page_count; page_num++) {
+               struct scrub_page *page_bad = sblock_bad->pagev + page_num;
+
+               if (!page_bad->io_error)
                        continue;
+
+               for (mirror_index = 0;
+                    mirror_index < BTRFS_MAX_MIRRORS &&
+                    sblocks_for_recheck[mirror_index].page_count > 0;
+                    mirror_index++) {
+                       struct scrub_block *sblock_other = sblocks_for_recheck +
+                                                          mirror_index;
+                       struct scrub_page *page_other = sblock_other->pagev +
+                                                       page_num;
+
+                       if (!page_other->io_error) {
+                               ret = scrub_repair_page_from_good_copy(
+                                       sblock_bad, sblock_other, page_num, 0);
+                               if (0 == ret) {
+                                       page_bad->io_error = 0;
+                                       break; /* succeeded for this page */
+                               }
+                       }
                }
 
-               if (scrub_fixup_check(sbio, ix) == 0)
-                       break;
+               if (page_bad->io_error) {
+                       /* did not find a mirror to copy the page from */
+                       success = 0;
+               }
        }
-       if (i == bbio->num_stripes)
-               goto uncorrectable;
 
-       if (!sdev->readonly) {
-               /*
-                * bi_io_vec[ix].bv_page now contains good data, write it back
-                */
-               if (scrub_fixup_io(WRITE, sdev->dev->bdev,
-                                  (sbio->physical + ix * PAGE_SIZE) >> 9,
-                                  sbio->bio->bi_io_vec[ix].bv_page)) {
-                       /* I/O-error, writeback failed, give up */
-                       goto uncorrectable;
+       if (success) {
+               if (is_metadata || have_csum) {
+                       /*
+                        * need to verify the checksum now that all
+                        * sectors on disk are repaired (the write
+                        * request for data to be repaired is on its way).
+                        * Just be lazy and use scrub_recheck_block()
+                        * which re-reads the data before the checksum
+                        * is verified, but most likely the data comes out
+                        * of the page cache.
+                        */
+                       ret = scrub_recheck_block(fs_info, sblock_bad,
+                                                 is_metadata, have_csum, csum,
+                                                 generation, sdev->csum_size);
+                       if (!ret && !sblock_bad->header_error &&
+                           !sblock_bad->checksum_error &&
+                           sblock_bad->no_io_error_seen)
+                               goto corrected_error;
+                       else
+                               goto did_not_correct_error;
+               } else {
+corrected_error:
+                       spin_lock(&sdev->stat_lock);
+                       sdev->stat.corrected_errors++;
+                       spin_unlock(&sdev->stat_lock);
+                       printk_ratelimited(KERN_ERR
+                               "btrfs: fixed up error at logical %llu on dev %s\n",
+                               (unsigned long long)logical, sdev->dev->name);
                }
+       } else {
+did_not_correct_error:
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.uncorrectable_errors++;
+               spin_unlock(&sdev->stat_lock);
+               printk_ratelimited(KERN_ERR
+                       "btrfs: unable to fixup (regular) error at logical %llu on dev %s\n",
+                       (unsigned long long)logical, sdev->dev->name);
        }
 
-       kfree(bbio);
-       spin_lock(&sdev->stat_lock);
-       ++sdev->stat.corrected_errors;
-       spin_unlock(&sdev->stat_lock);
+out:
+       if (sblocks_for_recheck) {
+               for (mirror_index = 0; mirror_index < BTRFS_MAX_MIRRORS;
+                    mirror_index++) {
+                       struct scrub_block *sblock = sblocks_for_recheck +
+                                                    mirror_index;
+                       int page_index;
+
+                       for (page_index = 0; page_index < SCRUB_PAGES_PER_BIO;
+                            page_index++)
+                               if (sblock->pagev[page_index].page)
+                                       __free_page(
+                                               sblock->pagev[page_index].page);
+               }
+               kfree(sblocks_for_recheck);
+       }
 
-       printk_ratelimited(KERN_ERR "btrfs: fixed up error at logical %llu\n",
-                              (unsigned long long)logical);
-       return;
+       return 0;
+}
 
-uncorrectable:
-       kfree(bbio);
-       spin_lock(&sdev->stat_lock);
-       ++sdev->stat.uncorrectable_errors;
-       spin_unlock(&sdev->stat_lock);
+static int scrub_setup_recheck_block(struct scrub_dev *sdev,
+                                    struct btrfs_mapping_tree *map_tree,
+                                    u64 length, u64 logical,
+                                    struct scrub_block *sblocks_for_recheck)
+{
+       int page_index;
+       int mirror_index;
+       int ret;
+
+       /*
+        * note: the three members sdev, ref_count and outstanding_pages
+        * are not used (and not set) in the blocks that are used for
+        * the recheck procedure
+        */
+
+       page_index = 0;
+       while (length > 0) {
+               u64 sublen = min_t(u64, length, PAGE_SIZE);
+               u64 mapped_length = sublen;
+               struct btrfs_bio *bbio = NULL;
+
+               /*
+                * with a length of PAGE_SIZE, each returned stripe
+                * represents one mirror
+                */
+               ret = btrfs_map_block(map_tree, WRITE, logical, &mapped_length,
+                                     &bbio, 0);
+               if (ret || !bbio || mapped_length < sublen) {
+                       kfree(bbio);
+                       return -EIO;
+               }
 
-       printk_ratelimited(KERN_ERR "btrfs: unable to fixup (regular) error at "
-                               "logical %llu\n", (unsigned long long)logical);
+               BUG_ON(page_index >= SCRUB_PAGES_PER_BIO);
+               for (mirror_index = 0; mirror_index < (int)bbio->num_stripes;
+                    mirror_index++) {
+                       struct scrub_block *sblock;
+                       struct scrub_page *page;
+
+                       if (mirror_index >= BTRFS_MAX_MIRRORS)
+                               continue;
+
+                       sblock = sblocks_for_recheck + mirror_index;
+                       page = sblock->pagev + page_index;
+                       page->logical = logical;
+                       page->physical = bbio->stripes[mirror_index].physical;
+                       page->bdev = bbio->stripes[mirror_index].dev->bdev;
+                       page->mirror_num = mirror_index + 1;
+                       page->page = alloc_page(GFP_NOFS);
+                       if (!page->page) {
+                               spin_lock(&sdev->stat_lock);
+                               sdev->stat.malloc_errors++;
+                               spin_unlock(&sdev->stat_lock);
+                               return -ENOMEM;
+                       }
+                       sblock->page_count++;
+               }
+               kfree(bbio);
+               length -= sublen;
+               logical += sublen;
+               page_index++;
+       }
+
+       return 0;
 }
 
-static int scrub_fixup_io(int rw, struct block_device *bdev, sector_t sector,
-                        struct page *page)
+/*
+ * this function will check the on disk data for checksum errors, header
+ * errors and read I/O errors. If any I/O errors happen, the exact pages
+ * which are errored are marked as being bad. The goal is to enable scrub
+ * to take those pages that are not errored from all the mirrors so that
+ * the pages that are errored in the just handled mirror can be repaired.
+ */
+static int scrub_recheck_block(struct btrfs_fs_info *fs_info,
+                              struct scrub_block *sblock, int is_metadata,
+                              int have_csum, u8 *csum, u64 generation,
+                              u16 csum_size)
 {
-       struct bio *bio = NULL;
-       int ret;
-       DECLARE_COMPLETION_ONSTACK(complete);
+       int page_num;
+
+       sblock->no_io_error_seen = 1;
+       sblock->header_error = 0;
+       sblock->checksum_error = 0;
+
+       for (page_num = 0; page_num < sblock->page_count; page_num++) {
+               struct bio *bio;
+               int ret;
+               struct scrub_page *page = sblock->pagev + page_num;
+               DECLARE_COMPLETION_ONSTACK(complete);
+
+               BUG_ON(!page->page);
+               bio = bio_alloc(GFP_NOFS, 1);
+               bio->bi_bdev = page->bdev;
+               bio->bi_sector = page->physical >> 9;
+               bio->bi_end_io = scrub_complete_bio_end_io;
+               bio->bi_private = &complete;
+
+               ret = bio_add_page(bio, page->page, PAGE_SIZE, 0);
+               if (PAGE_SIZE != ret) {
+                       bio_put(bio);
+                       return -EIO;
+               }
+               btrfsic_submit_bio(READ, bio);
 
-       bio = bio_alloc(GFP_NOFS, 1);
-       bio->bi_bdev = bdev;
-       bio->bi_sector = sector;
-       bio_add_page(bio, page, PAGE_SIZE, 0);
-       bio->bi_end_io = scrub_fixup_end_io;
-       bio->bi_private = &complete;
-       btrfsic_submit_bio(rw, bio);
+               /* this will also unplug the queue */
+               wait_for_completion(&complete);
 
-       /* this will also unplug the queue */
-       wait_for_completion(&complete);
+               page->io_error = !test_bit(BIO_UPTODATE, &bio->bi_flags);
+               if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
+                       sblock->no_io_error_seen = 0;
+               bio_put(bio);
+       }
 
-       ret = !test_bit(BIO_UPTODATE, &bio->bi_flags);
-       bio_put(bio);
-       return ret;
+       if (sblock->no_io_error_seen)
+               scrub_recheck_block_checksum(fs_info, sblock, is_metadata,
+                                            have_csum, csum, generation,
+                                            csum_size);
+
+       return 0;
 }
 
-static void scrub_bio_end_io(struct bio *bio, int err)
+static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
+                                        struct scrub_block *sblock,
+                                        int is_metadata, int have_csum,
+                                        const u8 *csum, u64 generation,
+                                        u16 csum_size)
 {
-       struct scrub_bio *sbio = bio->bi_private;
-       struct scrub_dev *sdev = sbio->sdev;
-       struct btrfs_fs_info *fs_info = sdev->dev->dev_root->fs_info;
+       int page_num;
+       u8 calculated_csum[BTRFS_CSUM_SIZE];
+       u32 crc = ~(u32)0;
+       struct btrfs_root *root = fs_info->extent_root;
+       void *mapped_buffer;
+
+       BUG_ON(!sblock->pagev[0].page);
+       if (is_metadata) {
+               struct btrfs_header *h;
+
+               mapped_buffer = kmap_atomic(sblock->pagev[0].page);
+               h = (struct btrfs_header *)mapped_buffer;
+
+               if (sblock->pagev[0].logical != le64_to_cpu(h->bytenr) ||
+                   generation != le64_to_cpu(h->generation) ||
+                   memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE) ||
+                   memcmp(h->chunk_tree_uuid, fs_info->chunk_tree_uuid,
+                          BTRFS_UUID_SIZE))
+                       sblock->header_error = 1;
+               csum = h->csum;
+       } else {
+               if (!have_csum)
+                       return;
 
-       sbio->err = err;
-       sbio->bio = bio;
+               mapped_buffer = kmap_atomic(sblock->pagev[0].page);
+       }
 
-       btrfs_queue_worker(&fs_info->scrub_workers, &sbio->work);
+       for (page_num = 0;;) {
+               if (page_num == 0 && is_metadata)
+                       crc = btrfs_csum_data(root,
+                               ((u8 *)mapped_buffer) + BTRFS_CSUM_SIZE,
+                               crc, PAGE_SIZE - BTRFS_CSUM_SIZE);
+               else
+                       crc = btrfs_csum_data(root, mapped_buffer, crc,
+                                             PAGE_SIZE);
+
+               kunmap_atomic(mapped_buffer);
+               page_num++;
+               if (page_num >= sblock->page_count)
+                       break;
+               BUG_ON(!sblock->pagev[page_num].page);
+
+               mapped_buffer = kmap_atomic(sblock->pagev[page_num].page);
+       }
+
+       btrfs_csum_final(crc, calculated_csum);
+       if (memcmp(calculated_csum, csum, csum_size))
+               sblock->checksum_error = 1;
 }
 
-static void scrub_checksum(struct btrfs_work *work)
+static void scrub_complete_bio_end_io(struct bio *bio, int err)
 {
-       struct scrub_bio *sbio = container_of(work, struct scrub_bio, work);
-       struct scrub_dev *sdev = sbio->sdev;
-       struct page *page;
-       void *buffer;
-       int i;
-       u64 flags;
-       u64 logical;
-       int ret;
+       complete((struct completion *)bio->bi_private);
+}
 
-       if (sbio->err) {
-               ret = 0;
-               for (i = 0; i < sbio->count; ++i)
-                       ret |= scrub_recheck_error(sbio, i);
-               if (!ret) {
-                       spin_lock(&sdev->stat_lock);
-                       ++sdev->stat.unverified_errors;
-                       spin_unlock(&sdev->stat_lock);
-               }
+static int scrub_repair_block_from_good_copy(struct scrub_block *sblock_bad,
+                                            struct scrub_block *sblock_good,
+                                            int force_write)
+{
+       int page_num;
+       int ret = 0;
 
-               sbio->bio->bi_flags &= ~(BIO_POOL_MASK - 1);
-               sbio->bio->bi_flags |= 1 << BIO_UPTODATE;
-               sbio->bio->bi_phys_segments = 0;
-               sbio->bio->bi_idx = 0;
+       for (page_num = 0; page_num < sblock_bad->page_count; page_num++) {
+               int ret_sub;
 
-               for (i = 0; i < sbio->count; i++) {
-                       struct bio_vec *bi;
-                       bi = &sbio->bio->bi_io_vec[i];
-                       bi->bv_offset = 0;
-                       bi->bv_len = PAGE_SIZE;
-               }
-               goto out;
+               ret_sub = scrub_repair_page_from_good_copy(sblock_bad,
+                                                          sblock_good,
+                                                          page_num,
+                                                          force_write);
+               if (ret_sub)
+                       ret = ret_sub;
        }
-       for (i = 0; i < sbio->count; ++i) {
-               page = sbio->bio->bi_io_vec[i].bv_page;
-               buffer = kmap_atomic(page);
-               flags = sbio->spag[i].flags;
-               logical = sbio->logical + i * PAGE_SIZE;
-               ret = 0;
-               if (flags & BTRFS_EXTENT_FLAG_DATA) {
-                       ret = scrub_checksum_data(sdev, sbio->spag + i, buffer);
-               } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
-                       ret = scrub_checksum_tree_block(sdev, sbio->spag + i,
-                                                       logical, buffer);
-               } else if (flags & BTRFS_EXTENT_FLAG_SUPER) {
-                       BUG_ON(i);
-                       (void)scrub_checksum_super(sbio, buffer);
-               } else {
-                       WARN_ON(1);
-               }
-               kunmap_atomic(buffer);
-               if (ret) {
-                       ret = scrub_recheck_error(sbio, i);
-                       if (!ret) {
-                               spin_lock(&sdev->stat_lock);
-                               ++sdev->stat.unverified_errors;
-                               spin_unlock(&sdev->stat_lock);
-                       }
+
+       return ret;
+}
+
+static int scrub_repair_page_from_good_copy(struct scrub_block *sblock_bad,
+                                           struct scrub_block *sblock_good,
+                                           int page_num, int force_write)
+{
+       struct scrub_page *page_bad = sblock_bad->pagev + page_num;
+       struct scrub_page *page_good = sblock_good->pagev + page_num;
+
+       BUG_ON(sblock_bad->pagev[page_num].page == NULL);
+       BUG_ON(sblock_good->pagev[page_num].page == NULL);
+       if (force_write || sblock_bad->header_error ||
+           sblock_bad->checksum_error || page_bad->io_error) {
+               struct bio *bio;
+               int ret;
+               DECLARE_COMPLETION_ONSTACK(complete);
+
+               bio = bio_alloc(GFP_NOFS, 1);
+               bio->bi_bdev = page_bad->bdev;
+               bio->bi_sector = page_bad->physical >> 9;
+               bio->bi_end_io = scrub_complete_bio_end_io;
+               bio->bi_private = &complete;
+
+               ret = bio_add_page(bio, page_good->page, PAGE_SIZE, 0);
+               if (PAGE_SIZE != ret) {
+                       bio_put(bio);
+                       return -EIO;
                }
+               btrfsic_submit_bio(WRITE, bio);
+
+               /* this will also unplug the queue */
+               wait_for_completion(&complete);
+               bio_put(bio);
        }
 
-out:
-       scrub_free_bio(sbio->bio);
-       sbio->bio = NULL;
-       spin_lock(&sdev->list_lock);
-       sbio->next_free = sdev->first_free;
-       sdev->first_free = sbio->index;
-       spin_unlock(&sdev->list_lock);
-       atomic_dec(&sdev->in_flight);
-       wake_up(&sdev->list_wait);
+       return 0;
+}
+
+static void scrub_checksum(struct scrub_block *sblock)
+{
+       u64 flags;
+       int ret;
+
+       BUG_ON(sblock->page_count < 1);
+       flags = sblock->pagev[0].flags;
+       ret = 0;
+       if (flags & BTRFS_EXTENT_FLAG_DATA)
+               ret = scrub_checksum_data(sblock);
+       else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)
+               ret = scrub_checksum_tree_block(sblock);
+       else if (flags & BTRFS_EXTENT_FLAG_SUPER)
+               (void)scrub_checksum_super(sblock);
+       else
+               WARN_ON(1);
+       if (ret)
+               scrub_handle_errored_block(sblock);
 }
 
-static int scrub_checksum_data(struct scrub_dev *sdev,
-                              struct scrub_page *spag, void *buffer)
+static int scrub_checksum_data(struct scrub_block *sblock)
 {
+       struct scrub_dev *sdev = sblock->sdev;
        u8 csum[BTRFS_CSUM_SIZE];
+       u8 *on_disk_csum;
+       struct page *page;
+       void *buffer;
        u32 crc = ~(u32)0;
        int fail = 0;
        struct btrfs_root *root = sdev->dev->dev_root;
+       u64 len;
+       int index;
 
-       if (!spag->have_csum)
+       BUG_ON(sblock->page_count < 1);
+       if (!sblock->pagev[0].have_csum)
                return 0;
 
-       crc = btrfs_csum_data(root, buffer, crc, PAGE_SIZE);
+       on_disk_csum = sblock->pagev[0].csum;
+       page = sblock->pagev[0].page;
+       buffer = kmap_atomic(page);
+
+       len = sdev->sectorsize;
+       index = 0;
+       for (;;) {
+               u64 l = min_t(u64, len, PAGE_SIZE);
+
+               crc = btrfs_csum_data(root, buffer, crc, l);
+               kunmap_atomic(buffer);
+               len -= l;
+               if (len == 0)
+                       break;
+               index++;
+               BUG_ON(index >= sblock->page_count);
+               BUG_ON(!sblock->pagev[index].page);
+               page = sblock->pagev[index].page;
+               buffer = kmap_atomic(page);
+       }
+
        btrfs_csum_final(crc, csum);
-       if (memcmp(csum, spag->csum, sdev->csum_size))
+       if (memcmp(csum, on_disk_csum, sdev->csum_size))
                fail = 1;
 
-       spin_lock(&sdev->stat_lock);
-       ++sdev->stat.data_extents_scrubbed;
-       sdev->stat.data_bytes_scrubbed += PAGE_SIZE;
-       if (fail)
+       if (fail) {
+               spin_lock(&sdev->stat_lock);
                ++sdev->stat.csum_errors;
-       spin_unlock(&sdev->stat_lock);
+               spin_unlock(&sdev->stat_lock);
+       }
 
        return fail;
 }
 
-static int scrub_checksum_tree_block(struct scrub_dev *sdev,
-                                    struct scrub_page *spag, u64 logical,
-                                    void *buffer)
+static int scrub_checksum_tree_block(struct scrub_block *sblock)
 {
+       struct scrub_dev *sdev = sblock->sdev;
        struct btrfs_header *h;
        struct btrfs_root *root = sdev->dev->dev_root;
        struct btrfs_fs_info *fs_info = root->fs_info;
-       u8 csum[BTRFS_CSUM_SIZE];
+       u8 calculated_csum[BTRFS_CSUM_SIZE];
+       u8 on_disk_csum[BTRFS_CSUM_SIZE];
+       struct page *page;
+       void *mapped_buffer;
+       u64 mapped_size;
+       void *p;
        u32 crc = ~(u32)0;
        int fail = 0;
        int crc_fail = 0;
+       u64 len;
+       int index;
+
+       BUG_ON(sblock->page_count < 1);
+       page = sblock->pagev[0].page;
+       mapped_buffer = kmap_atomic(page);
+       h = (struct btrfs_header *)mapped_buffer;
+       memcpy(on_disk_csum, h->csum, sdev->csum_size);
 
        /*
         * we don't use the getter functions here, as we
         * a) don't have an extent buffer and
         * b) the page is already kmapped
         */
-       h = (struct btrfs_header *)buffer;
 
-       if (logical != le64_to_cpu(h->bytenr))
+       if (sblock->pagev[0].logical != le64_to_cpu(h->bytenr))
                ++fail;
 
-       if (spag->generation != le64_to_cpu(h->generation))
+       if (sblock->pagev[0].generation != le64_to_cpu(h->generation))
                ++fail;
 
        if (memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE))
@@ -887,51 +1305,99 @@ static int scrub_checksum_tree_block(struct scrub_dev *sdev,
                   BTRFS_UUID_SIZE))
                ++fail;
 
-       crc = btrfs_csum_data(root, buffer + BTRFS_CSUM_SIZE, crc,
-                             PAGE_SIZE - BTRFS_CSUM_SIZE);
-       btrfs_csum_final(crc, csum);
-       if (memcmp(csum, h->csum, sdev->csum_size))
+       BUG_ON(sdev->nodesize != sdev->leafsize);
+       len = sdev->nodesize - BTRFS_CSUM_SIZE;
+       mapped_size = PAGE_SIZE - BTRFS_CSUM_SIZE;
+       p = ((u8 *)mapped_buffer) + BTRFS_CSUM_SIZE;
+       index = 0;
+       for (;;) {
+               u64 l = min_t(u64, len, mapped_size);
+
+               crc = btrfs_csum_data(root, p, crc, l);
+               kunmap_atomic(mapped_buffer);
+               len -= l;
+               if (len == 0)
+                       break;
+               index++;
+               BUG_ON(index >= sblock->page_count);
+               BUG_ON(!sblock->pagev[index].page);
+               page = sblock->pagev[index].page;
+               mapped_buffer = kmap_atomic(page);
+               mapped_size = PAGE_SIZE;
+               p = mapped_buffer;
+       }
+
+       btrfs_csum_final(crc, calculated_csum);
+       if (memcmp(calculated_csum, on_disk_csum, sdev->csum_size))
                ++crc_fail;
 
-       spin_lock(&sdev->stat_lock);
-       ++sdev->stat.tree_extents_scrubbed;
-       sdev->stat.tree_bytes_scrubbed += PAGE_SIZE;
-       if (crc_fail)
-               ++sdev->stat.csum_errors;
-       if (fail)
-               ++sdev->stat.verify_errors;
-       spin_unlock(&sdev->stat_lock);
+       if (crc_fail || fail) {
+               spin_lock(&sdev->stat_lock);
+               if (crc_fail)
+                       ++sdev->stat.csum_errors;
+               if (fail)
+                       ++sdev->stat.verify_errors;
+               spin_unlock(&sdev->stat_lock);
+       }
 
        return fail || crc_fail;
 }
 
-static int scrub_checksum_super(struct scrub_bio *sbio, void *buffer)
+static int scrub_checksum_super(struct scrub_block *sblock)
 {
        struct btrfs_super_block *s;
-       u64 logical;
-       struct scrub_dev *sdev = sbio->sdev;
+       struct scrub_dev *sdev = sblock->sdev;
        struct btrfs_root *root = sdev->dev->dev_root;
        struct btrfs_fs_info *fs_info = root->fs_info;
-       u8 csum[BTRFS_CSUM_SIZE];
+       u8 calculated_csum[BTRFS_CSUM_SIZE];
+       u8 on_disk_csum[BTRFS_CSUM_SIZE];
+       struct page *page;
+       void *mapped_buffer;
+       u64 mapped_size;
+       void *p;
        u32 crc = ~(u32)0;
        int fail = 0;
+       u64 len;
+       int index;
 
-       s = (struct btrfs_super_block *)buffer;
-       logical = sbio->logical;
+       BUG_ON(sblock->page_count < 1);
+       page = sblock->pagev[0].page;
+       mapped_buffer = kmap_atomic(page);
+       s = (struct btrfs_super_block *)mapped_buffer;
+       memcpy(on_disk_csum, s->csum, sdev->csum_size);
 
-       if (logical != le64_to_cpu(s->bytenr))
+       if (sblock->pagev[0].logical != le64_to_cpu(s->bytenr))
                ++fail;
 
-       if (sbio->spag[0].generation != le64_to_cpu(s->generation))
+       if (sblock->pagev[0].generation != le64_to_cpu(s->generation))
                ++fail;
 
        if (memcmp(s->fsid, fs_info->fsid, BTRFS_UUID_SIZE))
                ++fail;
 
-       crc = btrfs_csum_data(root, buffer + BTRFS_CSUM_SIZE, crc,
-                             PAGE_SIZE - BTRFS_CSUM_SIZE);
-       btrfs_csum_final(crc, csum);
-       if (memcmp(csum, s->csum, sbio->sdev->csum_size))
+       len = BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE;
+       mapped_size = PAGE_SIZE - BTRFS_CSUM_SIZE;
+       p = ((u8 *)mapped_buffer) + BTRFS_CSUM_SIZE;
+       index = 0;
+       for (;;) {
+               u64 l = min_t(u64, len, mapped_size);
+
+               crc = btrfs_csum_data(root, p, crc, l);
+               kunmap_atomic(mapped_buffer);
+               len -= l;
+               if (len == 0)
+                       break;
+               index++;
+               BUG_ON(index >= sblock->page_count);
+               BUG_ON(!sblock->pagev[index].page);
+               page = sblock->pagev[index].page;
+               mapped_buffer = kmap_atomic(page);
+               mapped_size = PAGE_SIZE;
+               p = mapped_buffer;
+       }
+
+       btrfs_csum_final(crc, calculated_csum);
+       if (memcmp(calculated_csum, on_disk_csum, sdev->csum_size))
                ++fail;
 
        if (fail) {
@@ -948,29 +1414,42 @@ static int scrub_checksum_super(struct scrub_bio *sbio, void *buffer)
        return fail;
 }
 
-static int scrub_submit(struct scrub_dev *sdev)
+static void scrub_block_get(struct scrub_block *sblock)
+{
+       atomic_inc(&sblock->ref_count);
+}
+
+static void scrub_block_put(struct scrub_block *sblock)
+{
+       if (atomic_dec_and_test(&sblock->ref_count)) {
+               int i;
+
+               for (i = 0; i < sblock->page_count; i++)
+                       if (sblock->pagev[i].page)
+                               __free_page(sblock->pagev[i].page);
+               kfree(sblock);
+       }
+}
+
+static void scrub_submit(struct scrub_dev *sdev)
 {
        struct scrub_bio *sbio;
 
        if (sdev->curr == -1)
-               return 0;
+               return;
 
        sbio = sdev->bios[sdev->curr];
-       sbio->err = 0;
        sdev->curr = -1;
        atomic_inc(&sdev->in_flight);
 
        btrfsic_submit_bio(READ, sbio->bio);
-
-       return 0;
 }
 
-static int scrub_page(struct scrub_dev *sdev, u64 logical, u64 len,
-                     u64 physical, u64 flags, u64 gen, int mirror_num,
-                     u8 *csum, int force)
+static int scrub_add_page_to_bio(struct scrub_dev *sdev,
+                                struct scrub_page *spage)
 {
+       struct scrub_block *sblock = spage->sblock;
        struct scrub_bio *sbio;
-       struct page *page;
        int ret;
 
 again:
@@ -983,7 +1462,7 @@ again:
                if (sdev->curr != -1) {
                        sdev->first_free = sdev->bios[sdev->curr]->next_free;
                        sdev->bios[sdev->curr]->next_free = -1;
-                       sdev->bios[sdev->curr]->count = 0;
+                       sdev->bios[sdev->curr]->page_count = 0;
                        spin_unlock(&sdev->list_lock);
                } else {
                        spin_unlock(&sdev->list_lock);
@@ -991,62 +1470,200 @@ again:
                }
        }
        sbio = sdev->bios[sdev->curr];
-       if (sbio->count == 0) {
+       if (sbio->page_count == 0) {
                struct bio *bio;
 
-               sbio->physical = physical;
-               sbio->logical = logical;
-               bio = bio_alloc(GFP_NOFS, SCRUB_PAGES_PER_BIO);
-               if (!bio)
-                       return -ENOMEM;
+               sbio->physical = spage->physical;
+               sbio->logical = spage->logical;
+               bio = sbio->bio;
+               if (!bio) {
+                       bio = bio_alloc(GFP_NOFS, sdev->pages_per_bio);
+                       if (!bio)
+                               return -ENOMEM;
+                       sbio->bio = bio;
+               }
 
                bio->bi_private = sbio;
                bio->bi_end_io = scrub_bio_end_io;
                bio->bi_bdev = sdev->dev->bdev;
-               bio->bi_sector = sbio->physical >> 9;
+               bio->bi_sector = spage->physical >> 9;
                sbio->err = 0;
-               sbio->bio = bio;
-       } else if (sbio->physical + sbio->count * PAGE_SIZE != physical ||
-                  sbio->logical + sbio->count * PAGE_SIZE != logical) {
-               ret = scrub_submit(sdev);
-               if (ret)
-                       return ret;
+       } else if (sbio->physical + sbio->page_count * PAGE_SIZE !=
+                  spage->physical ||
+                  sbio->logical + sbio->page_count * PAGE_SIZE !=
+                  spage->logical) {
+               scrub_submit(sdev);
                goto again;
        }
-       sbio->spag[sbio->count].flags = flags;
-       sbio->spag[sbio->count].generation = gen;
-       sbio->spag[sbio->count].have_csum = 0;
-       sbio->spag[sbio->count].mirror_num = mirror_num;
-
-       page = alloc_page(GFP_NOFS);
-       if (!page)
-               return -ENOMEM;
 
-       ret = bio_add_page(sbio->bio, page, PAGE_SIZE, 0);
-       if (!ret) {
-               __free_page(page);
-               ret = scrub_submit(sdev);
-               if (ret)
-                       return ret;
+       sbio->pagev[sbio->page_count] = spage;
+       ret = bio_add_page(sbio->bio, spage->page, PAGE_SIZE, 0);
+       if (ret != PAGE_SIZE) {
+               if (sbio->page_count < 1) {
+                       bio_put(sbio->bio);
+                       sbio->bio = NULL;
+                       return -EIO;
+               }
+               scrub_submit(sdev);
                goto again;
        }
 
-       if (csum) {
-               sbio->spag[sbio->count].have_csum = 1;
-               memcpy(sbio->spag[sbio->count].csum, csum, sdev->csum_size);
+       scrub_block_get(sblock); /* one for the added page */
+       atomic_inc(&sblock->outstanding_pages);
+       sbio->page_count++;
+       if (sbio->page_count == sdev->pages_per_bio)
+               scrub_submit(sdev);
+
+       return 0;
+}
+
+static int scrub_pages(struct scrub_dev *sdev, u64 logical, u64 len,
+                      u64 physical, u64 flags, u64 gen, int mirror_num,
+                      u8 *csum, int force)
+{
+       struct scrub_block *sblock;
+       int index;
+
+       sblock = kzalloc(sizeof(*sblock), GFP_NOFS);
+       if (!sblock) {
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.malloc_errors++;
+               spin_unlock(&sdev->stat_lock);
+               return -ENOMEM;
        }
-       ++sbio->count;
-       if (sbio->count == SCRUB_PAGES_PER_BIO || force) {
+
+       /* one ref inside this function, plus one for each page later on */
+       atomic_set(&sblock->ref_count, 1);
+       sblock->sdev = sdev;
+       sblock->no_io_error_seen = 1;
+
+       for (index = 0; len > 0; index++) {
+               struct scrub_page *spage = sblock->pagev + index;
+               u64 l = min_t(u64, len, PAGE_SIZE);
+
+               BUG_ON(index >= SCRUB_MAX_PAGES_PER_BLOCK);
+               spage->page = alloc_page(GFP_NOFS);
+               if (!spage->page) {
+                       spin_lock(&sdev->stat_lock);
+                       sdev->stat.malloc_errors++;
+                       spin_unlock(&sdev->stat_lock);
+                       while (index > 0) {
+                               index--;
+                               __free_page(sblock->pagev[index].page);
+                       }
+                       kfree(sblock);
+                       return -ENOMEM;
+               }
+               spage->sblock = sblock;
+               spage->bdev = sdev->dev->bdev;
+               spage->flags = flags;
+               spage->generation = gen;
+               spage->logical = logical;
+               spage->physical = physical;
+               spage->mirror_num = mirror_num;
+               if (csum) {
+                       spage->have_csum = 1;
+                       memcpy(spage->csum, csum, sdev->csum_size);
+               } else {
+                       spage->have_csum = 0;
+               }
+               sblock->page_count++;
+               len -= l;
+               logical += l;
+               physical += l;
+       }
+
+       BUG_ON(sblock->page_count == 0);
+       for (index = 0; index < sblock->page_count; index++) {
+               struct scrub_page *spage = sblock->pagev + index;
                int ret;
 
-               ret = scrub_submit(sdev);
-               if (ret)
+               ret = scrub_add_page_to_bio(sdev, spage);
+               if (ret) {
+                       scrub_block_put(sblock);
                        return ret;
+               }
        }
 
+       if (force)
+               scrub_submit(sdev);
+
+       /* last one frees, either here or in bio completion for last page */
+       scrub_block_put(sblock);
        return 0;
 }
 
+static void scrub_bio_end_io(struct bio *bio, int err)
+{
+       struct scrub_bio *sbio = bio->bi_private;
+       struct scrub_dev *sdev = sbio->sdev;
+       struct btrfs_fs_info *fs_info = sdev->dev->dev_root->fs_info;
+
+       sbio->err = err;
+       sbio->bio = bio;
+
+       btrfs_queue_worker(&fs_info->scrub_workers, &sbio->work);
+}
+
+static void scrub_bio_end_io_worker(struct btrfs_work *work)
+{
+       struct scrub_bio *sbio = container_of(work, struct scrub_bio, work);
+       struct scrub_dev *sdev = sbio->sdev;
+       int i;
+
+       BUG_ON(sbio->page_count > SCRUB_PAGES_PER_BIO);
+       if (sbio->err) {
+               for (i = 0; i < sbio->page_count; i++) {
+                       struct scrub_page *spage = sbio->pagev[i];
+
+                       spage->io_error = 1;
+                       spage->sblock->no_io_error_seen = 0;
+               }
+       }
+
+       /* now complete the scrub_block items that have all pages completed */
+       for (i = 0; i < sbio->page_count; i++) {
+               struct scrub_page *spage = sbio->pagev[i];
+               struct scrub_block *sblock = spage->sblock;
+
+               if (atomic_dec_and_test(&sblock->outstanding_pages))
+                       scrub_block_complete(sblock);
+               scrub_block_put(sblock);
+       }
+
+       if (sbio->err) {
+               /* what is this good for??? */
+               sbio->bio->bi_flags &= ~(BIO_POOL_MASK - 1);
+               sbio->bio->bi_flags |= 1 << BIO_UPTODATE;
+               sbio->bio->bi_phys_segments = 0;
+               sbio->bio->bi_idx = 0;
+
+               for (i = 0; i < sbio->page_count; i++) {
+                       struct bio_vec *bi;
+                       bi = &sbio->bio->bi_io_vec[i];
+                       bi->bv_offset = 0;
+                       bi->bv_len = PAGE_SIZE;
+               }
+       }
+
+       bio_put(sbio->bio);
+       sbio->bio = NULL;
+       spin_lock(&sdev->list_lock);
+       sbio->next_free = sdev->first_free;
+       sdev->first_free = sbio->index;
+       spin_unlock(&sdev->list_lock);
+       atomic_dec(&sdev->in_flight);
+       wake_up(&sdev->list_wait);
+}
+
+static void scrub_block_complete(struct scrub_block *sblock)
+{
+       if (!sblock->no_io_error_seen)
+               scrub_handle_errored_block(sblock);
+       else
+               scrub_checksum(sblock);
+}
+
 static int scrub_find_csum(struct scrub_dev *sdev, u64 logical, u64 len,
                           u8 *csum)
 {
@@ -1054,7 +1671,6 @@ static int scrub_find_csum(struct scrub_dev *sdev, u64 logical, u64 len,
        int ret = 0;
        unsigned long i;
        unsigned long num_sectors;
-       u32 sectorsize = sdev->dev->dev_root->sectorsize;
 
        while (!list_empty(&sdev->csum_list)) {
                sum = list_first_entry(&sdev->csum_list,
@@ -1072,7 +1688,7 @@ static int scrub_find_csum(struct scrub_dev *sdev, u64 logical, u64 len,
        if (!sum)
                return 0;
 
-       num_sectors = sum->len / sectorsize;
+       num_sectors = sum->len / sdev->sectorsize;
        for (i = 0; i < num_sectors; ++i) {
                if (sum->sums[i].bytenr == logical) {
                        memcpy(csum, &sum->sums[i].sum, sdev->csum_size);
@@ -1093,9 +1709,28 @@ static int scrub_extent(struct scrub_dev *sdev, u64 logical, u64 len,
 {
        int ret;
        u8 csum[BTRFS_CSUM_SIZE];
+       u32 blocksize;
+
+       if (flags & BTRFS_EXTENT_FLAG_DATA) {
+               blocksize = sdev->sectorsize;
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.data_extents_scrubbed++;
+               sdev->stat.data_bytes_scrubbed += len;
+               spin_unlock(&sdev->stat_lock);
+       } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+               BUG_ON(sdev->nodesize != sdev->leafsize);
+               blocksize = sdev->nodesize;
+               spin_lock(&sdev->stat_lock);
+               sdev->stat.tree_extents_scrubbed++;
+               sdev->stat.tree_bytes_scrubbed += len;
+               spin_unlock(&sdev->stat_lock);
+       } else {
+               blocksize = sdev->sectorsize;
+               BUG_ON(1);
+       }
 
        while (len) {
-               u64 l = min_t(u64, len, PAGE_SIZE);
+               u64 l = min_t(u64, len, blocksize);
                int have_csum = 0;
 
                if (flags & BTRFS_EXTENT_FLAG_DATA) {
@@ -1104,8 +1739,8 @@ static int scrub_extent(struct scrub_dev *sdev, u64 logical, u64 len,
                        if (have_csum == 0)
                                ++sdev->stat.no_csum;
                }
-               ret = scrub_page(sdev, logical, l, physical, flags, gen,
-                                mirror_num, have_csum ? csum : NULL, 0);
+               ret = scrub_pages(sdev, logical, l, physical, flags, gen,
+                                 mirror_num, have_csum ? csum : NULL, 0);
                if (ret)
                        return ret;
                len -= l;
@@ -1170,6 +1805,11 @@ static noinline_for_stack int scrub_stripe(struct scrub_dev *sdev,
        if (!path)
                return -ENOMEM;
 
+       /*
+        * work on commit root. The related disk blocks are static as
+        * long as COW is applied. This means, it is save to rewrite
+        * them to repair disk errors without any race conditions
+        */
        path->search_commit_root = 1;
        path->skip_locking = 1;
 
@@ -1516,15 +2156,18 @@ static noinline_for_stack int scrub_supers(struct scrub_dev *sdev)
        struct btrfs_device *device = sdev->dev;
        struct btrfs_root *root = device->dev_root;
 
+       if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR)
+               return -EIO;
+
        gen = root->fs_info->last_trans_committed;
 
        for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
                bytenr = btrfs_sb_offset(i);
-               if (bytenr + BTRFS_SUPER_INFO_SIZE >= device->total_bytes)
+               if (bytenr + BTRFS_SUPER_INFO_SIZE > device->total_bytes)
                        break;
 
-               ret = scrub_page(sdev, bytenr, PAGE_SIZE, bytenr,
-                                BTRFS_EXTENT_FLAG_SUPER, gen, i, NULL, 1);
+               ret = scrub_pages(sdev, bytenr, BTRFS_SUPER_INFO_SIZE, bytenr,
+                                    BTRFS_EXTENT_FLAG_SUPER, gen, i, NULL, 1);
                if (ret)
                        return ret;
        }
@@ -1583,10 +2226,30 @@ int btrfs_scrub_dev(struct btrfs_root *root, u64 devid, u64 start, u64 end,
        /*
         * check some assumptions
         */
-       if (root->sectorsize != PAGE_SIZE ||
-           root->sectorsize != root->leafsize ||
-           root->sectorsize != root->nodesize) {
-               printk(KERN_ERR "btrfs_scrub: size assumptions fail\n");
+       if (root->nodesize != root->leafsize) {
+               printk(KERN_ERR
+                      "btrfs_scrub: size assumption nodesize == leafsize (%d == %d) fails\n",
+                      root->nodesize, root->leafsize);
+               return -EINVAL;
+       }
+
+       if (root->nodesize > BTRFS_STRIPE_LEN) {
+               /*
+                * in this case scrub is unable to calculate the checksum
+                * the way scrub is implemented. Do not handle this
+                * situation at all because it won't ever happen.
+                */
+               printk(KERN_ERR
+                      "btrfs_scrub: size assumption nodesize <= BTRFS_STRIPE_LEN (%d <= %d) fails\n",
+                      root->nodesize, BTRFS_STRIPE_LEN);
+               return -EINVAL;
+       }
+
+       if (root->sectorsize != PAGE_SIZE) {
+               /* not supported for data w/o checksums */
+               printk(KERN_ERR
+                      "btrfs_scrub: size assumption sectorsize != PAGE_SIZE (%d != %lld) fails\n",
+                      root->sectorsize, (unsigned long long)PAGE_SIZE);
                return -EINVAL;
        }
 
@@ -1656,7 +2319,7 @@ int btrfs_scrub_dev(struct btrfs_root *root, u64 devid, u64 start, u64 end,
        return ret;
 }
 
-int btrfs_scrub_pause(struct btrfs_root *root)
+void btrfs_scrub_pause(struct btrfs_root *root)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
 
@@ -1671,34 +2334,28 @@ int btrfs_scrub_pause(struct btrfs_root *root)
                mutex_lock(&fs_info->scrub_lock);
        }
        mutex_unlock(&fs_info->scrub_lock);
-
-       return 0;
 }
 
-int btrfs_scrub_continue(struct btrfs_root *root)
+void btrfs_scrub_continue(struct btrfs_root *root)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
 
        atomic_dec(&fs_info->scrub_pause_req);
        wake_up(&fs_info->scrub_pause_wait);
-       return 0;
 }
 
-int btrfs_scrub_pause_super(struct btrfs_root *root)
+void btrfs_scrub_pause_super(struct btrfs_root *root)
 {
        down_write(&root->fs_info->scrub_super_lock);
-       return 0;
 }
 
-int btrfs_scrub_continue_super(struct btrfs_root *root)
+void btrfs_scrub_continue_super(struct btrfs_root *root)
 {
        up_write(&root->fs_info->scrub_super_lock);
-       return 0;
 }
 
-int btrfs_scrub_cancel(struct btrfs_root *root)
+int __btrfs_scrub_cancel(struct btrfs_fs_info *fs_info)
 {
-       struct btrfs_fs_info *fs_info = root->fs_info;
 
        mutex_lock(&fs_info->scrub_lock);
        if (!atomic_read(&fs_info->scrubs_running)) {
@@ -1719,6 +2376,11 @@ int btrfs_scrub_cancel(struct btrfs_root *root)
        return 0;
 }
 
+int btrfs_scrub_cancel(struct btrfs_root *root)
+{
+       return __btrfs_scrub_cancel(root->fs_info);
+}
+
 int btrfs_scrub_cancel_dev(struct btrfs_root *root, struct btrfs_device *dev)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -1741,6 +2403,7 @@ int btrfs_scrub_cancel_dev(struct btrfs_root *root, struct btrfs_device *dev)
 
        return 0;
 }
+
 int btrfs_scrub_cancel_devid(struct btrfs_root *root, u64 devid)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
index bc1f6ad18442bc728a10e9919b91162af1b60b4d..c6ffa58124192a9e069b1464d863b33300bcf6e9 100644 (file)
@@ -44,8 +44,9 @@
 #define BTRFS_SETGET_FUNCS(name, type, member, bits)                   \
 u##bits btrfs_##name(struct extent_buffer *eb, type *s);               \
 void btrfs_set_##name(struct extent_buffer *eb, type *s, u##bits val); \
-u##bits btrfs_##name(struct extent_buffer *eb,                         \
-                                  type *s)                             \
+void btrfs_set_token_##name(struct extent_buffer *eb, type *s, u##bits val, struct btrfs_map_token *token);    \
+u##bits btrfs_token_##name(struct extent_buffer *eb,                           \
+                          type *s, struct btrfs_map_token *token)      \
 {                                                                      \
        unsigned long part_offset = (unsigned long)s;                   \
        unsigned long offset = part_offset + offsetof(type, member);    \
@@ -54,9 +55,18 @@ u##bits btrfs_##name(struct extent_buffer *eb,                               \
        char *kaddr;                                            \
        unsigned long map_start;                                \
        unsigned long map_len;                                  \
+       unsigned long mem_len = sizeof(((type *)0)->member);    \
        u##bits res;                                            \
+       if (token && token->kaddr && token->offset <= offset && \
+           token->eb == eb &&                                  \
+          (token->offset + PAGE_CACHE_SIZE >= offset + mem_len)) { \
+               kaddr = token->kaddr;                           \
+               p = (type *)(kaddr + part_offset - token->offset);  \
+               res = le##bits##_to_cpu(p->member);             \
+               return res;                                     \
+       }                                                       \
        err = map_private_extent_buffer(eb, offset,             \
-                       sizeof(((type *)0)->member),            \
+                       mem_len,                                \
                        &kaddr, &map_start, &map_len);          \
        if (err) {                                              \
                __le##bits leres;                               \
@@ -65,10 +75,15 @@ u##bits btrfs_##name(struct extent_buffer *eb,                              \
        }                                                       \
        p = (type *)(kaddr + part_offset - map_start);          \
        res = le##bits##_to_cpu(p->member);                     \
+       if (token) {                                            \
+               token->kaddr = kaddr;                           \
+               token->offset = map_start;                      \
+               token->eb = eb;                                 \
+       }                                                       \
        return res;                                             \
 }                                                                      \
-void btrfs_set_##name(struct extent_buffer *eb,                                \
-                                   type *s, u##bits val)               \
+void btrfs_set_token_##name(struct extent_buffer *eb,                          \
+                           type *s, u##bits val, struct btrfs_map_token *token)                \
 {                                                                      \
        unsigned long part_offset = (unsigned long)s;                   \
        unsigned long offset = part_offset + offsetof(type, member);    \
@@ -77,8 +92,17 @@ void btrfs_set_##name(struct extent_buffer *eb,                              \
        char *kaddr;                                            \
        unsigned long map_start;                                \
        unsigned long map_len;                                  \
+       unsigned long mem_len = sizeof(((type *)0)->member);    \
+       if (token && token->kaddr && token->offset <= offset && \
+           token->eb == eb &&                                  \
+          (token->offset + PAGE_CACHE_SIZE >= offset + mem_len)) { \
+               kaddr = token->kaddr;                           \
+               p = (type *)(kaddr + part_offset - token->offset);  \
+               p->member = cpu_to_le##bits(val);               \
+               return;                                         \
+       }                                                       \
        err = map_private_extent_buffer(eb, offset,             \
-                       sizeof(((type *)0)->member),            \
+                       mem_len,                                \
                        &kaddr, &map_start, &map_len);          \
        if (err) {                                              \
                __le##bits val2;                                \
@@ -88,7 +112,22 @@ void btrfs_set_##name(struct extent_buffer *eb,                             \
        }                                                       \
        p = (type *)(kaddr + part_offset - map_start);          \
        p->member = cpu_to_le##bits(val);                       \
-}
+       if (token) {                                            \
+               token->kaddr = kaddr;                           \
+               token->offset = map_start;                      \
+               token->eb = eb;                                 \
+       }                                                       \
+}                                                              \
+void btrfs_set_##name(struct extent_buffer *eb,                        \
+                     type *s, u##bits val)                     \
+{                                                              \
+       btrfs_set_token_##name(eb, s, val, NULL);               \
+}                                                              \
+u##bits btrfs_##name(struct extent_buffer *eb,                 \
+                     type *s)                                  \
+{                                                              \
+       return btrfs_token_##name(eb, s, NULL);                 \
+}                                                              \
 
 #include "ctree.h"
 
index 81df3fec6a6d24b0ebc3e93ff449f2244f21e832..8d5d380f7bdb8a81b576c51ac487ccc492051d2f 100644 (file)
@@ -76,6 +76,9 @@ static const char *btrfs_decode_error(struct btrfs_fs_info *fs_info, int errno,
        case -EROFS:
                errstr = "Readonly filesystem";
                break;
+       case -EEXIST:
+               errstr = "Object already exists";
+               break;
        default:
                if (nbuf) {
                        if (snprintf(nbuf, 16, "error %d", -errno) >= 0)
@@ -116,6 +119,8 @@ static void btrfs_handle_error(struct btrfs_fs_info *fs_info)
        if (fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
                sb->s_flags |= MS_RDONLY;
                printk(KERN_INFO "btrfs is forced readonly\n");
+               __btrfs_scrub_cancel(fs_info);
+//             WARN_ON(1);
        }
 }
 
@@ -124,25 +129,132 @@ static void btrfs_handle_error(struct btrfs_fs_info *fs_info)
  * invokes the approciate error response.
  */
 void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function,
-                    unsigned int line, int errno)
+                      unsigned int line, int errno, const char *fmt, ...)
 {
        struct super_block *sb = fs_info->sb;
        char nbuf[16];
        const char *errstr;
+       va_list args;
+       va_start(args, fmt);
 
        /*
         * Special case: if the error is EROFS, and we're already
         * under MS_RDONLY, then it is safe here.
         */
        if (errno == -EROFS && (sb->s_flags & MS_RDONLY))
+               return;
+
+       errstr = btrfs_decode_error(fs_info, errno, nbuf);
+       if (fmt) {
+               struct va_format vaf = {
+                       .fmt = fmt,
+                       .va = &args,
+               };
+
+               printk(KERN_CRIT "BTRFS error (device %s) in %s:%d: %s (%pV)\n",
+                       sb->s_id, function, line, errstr, &vaf);
+       } else {
+               printk(KERN_CRIT "BTRFS error (device %s) in %s:%d: %s\n",
+                       sb->s_id, function, line, errstr);
+       }
+
+       /* Don't go through full error handling during mount */
+       if (sb->s_flags & MS_BORN) {
+               save_error_info(fs_info);
+               btrfs_handle_error(fs_info);
+       }
+       va_end(args);
+}
+
+const char *logtypes[] = {
+       "emergency",
+       "alert",
+       "critical",
+       "error",
+       "warning",
+       "notice",
+       "info",
+       "debug",
+};
+
+void btrfs_printk(struct btrfs_fs_info *fs_info, const char *fmt, ...)
+{
+       struct super_block *sb = fs_info->sb;
+       char lvl[4];
+       struct va_format vaf;
+       va_list args;
+       const char *type = logtypes[4];
+
+       va_start(args, fmt);
+
+       if (fmt[0] == '<' && isdigit(fmt[1]) && fmt[2] == '>') {
+               strncpy(lvl, fmt, 3);
+               fmt += 3;
+               type = logtypes[fmt[1] - '0'];
+       } else
+               *lvl = '\0';
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+       printk("%sBTRFS %s (device %s): %pV", lvl, type, sb->s_id, &vaf);
+}
+
+/*
+ * We only mark the transaction aborted and then set the file system read-only.
+ * This will prevent new transactions from starting or trying to join this
+ * one.
+ *
+ * This means that error recovery at the call site is limited to freeing
+ * any local memory allocations and passing the error code up without
+ * further cleanup. The transaction should complete as it normally would
+ * in the call path but will return -EIO.
+ *
+ * We'll complete the cleanup in btrfs_end_transaction and
+ * btrfs_commit_transaction.
+ */
+void __btrfs_abort_transaction(struct btrfs_trans_handle *trans,
+                              struct btrfs_root *root, const char *function,
+                              unsigned int line, int errno)
+{
+       WARN_ONCE(1, KERN_DEBUG "btrfs: Transaction aborted");
+       trans->aborted = errno;
+       /* Nothing used. The other threads that have joined this
+        * transaction may be able to continue. */
+       if (!trans->blocks_used) {
+               btrfs_printk(root->fs_info, "Aborting unused transaction.\n");
                return;
+       }
+       trans->transaction->aborted = errno;
+       __btrfs_std_error(root->fs_info, function, line, errno, NULL);
+}
+/*
+ * __btrfs_panic decodes unexpected, fatal errors from the caller,
+ * issues an alert, and either panics or BUGs, depending on mount options.
+ */
+void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function,
+                  unsigned int line, int errno, const char *fmt, ...)
+{
+       char nbuf[16];
+       char *s_id = "<unknown>";
+       const char *errstr;
+       struct va_format vaf = { .fmt = fmt };
+       va_list args;
 
-       errstr = btrfs_decode_error(fs_info, errno, nbuf);
-       printk(KERN_CRIT "BTRFS error (device %s) in %s:%d: %s\n",
-               sb->s_id, function, line, errstr);
-       save_error_info(fs_info);
+       if (fs_info)
+               s_id = fs_info->sb->s_id;
 
-       btrfs_handle_error(fs_info);
+       va_start(args, fmt);
+       vaf.va = &args;
+
+       errstr = btrfs_decode_error(fs_info, errno, nbuf);
+       if (fs_info->mount_opt & BTRFS_MOUNT_PANIC_ON_FATAL_ERROR)
+               panic(KERN_CRIT "BTRFS panic (device %s) in %s:%d: %pV (%s)\n",
+                       s_id, function, line, &vaf, errstr);
+
+       printk(KERN_CRIT "BTRFS panic (device %s) in %s:%d: %pV (%s)\n",
+              s_id, function, line, &vaf, errstr);
+       va_end(args);
+       /* Caller calls BUG() */
 }
 
 static void btrfs_put_super(struct super_block *sb)
@@ -166,7 +278,7 @@ enum {
        Opt_enospc_debug, Opt_subvolrootid, Opt_defrag, Opt_inode_cache,
        Opt_no_space_cache, Opt_recovery, Opt_skip_balance,
        Opt_check_integrity, Opt_check_integrity_including_extent_data,
-       Opt_check_integrity_print_mask,
+       Opt_check_integrity_print_mask, Opt_fatal_errors,
        Opt_err,
 };
 
@@ -206,12 +318,14 @@ static match_table_t tokens = {
        {Opt_check_integrity, "check_int"},
        {Opt_check_integrity_including_extent_data, "check_int_data"},
        {Opt_check_integrity_print_mask, "check_int_print_mask=%d"},
+       {Opt_fatal_errors, "fatal_errors=%s"},
        {Opt_err, NULL},
 };
 
 /*
  * Regular mount options parser.  Everything that is needed only when
  * reading in a new superblock is parsed here.
+ * XXX JDM: This needs to be cleaned up for remount.
  */
 int btrfs_parse_options(struct btrfs_root *root, char *options)
 {
@@ -438,6 +552,18 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
                        ret = -EINVAL;
                        goto out;
 #endif
+               case Opt_fatal_errors:
+                       if (strcmp(args[0].from, "panic") == 0)
+                               btrfs_set_opt(info->mount_opt,
+                                             PANIC_ON_FATAL_ERROR);
+                       else if (strcmp(args[0].from, "bug") == 0)
+                               btrfs_clear_opt(info->mount_opt,
+                                             PANIC_ON_FATAL_ERROR);
+                       else {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+                       break;
                case Opt_err:
                        printk(KERN_INFO "btrfs: unrecognized mount option "
                               "'%s'\n", p);
@@ -762,6 +888,8 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
                seq_puts(seq, ",inode_cache");
        if (btrfs_test_opt(root, SKIP_BALANCE))
                seq_puts(seq, ",skip_balance");
+       if (btrfs_test_opt(root, PANIC_ON_FATAL_ERROR))
+               seq_puts(seq, ",fatal_errors=panic");
        return 0;
 }
 
@@ -995,11 +1123,20 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(sb);
        struct btrfs_root *root = fs_info->tree_root;
+       unsigned old_flags = sb->s_flags;
+       unsigned long old_opts = fs_info->mount_opt;
+       unsigned long old_compress_type = fs_info->compress_type;
+       u64 old_max_inline = fs_info->max_inline;
+       u64 old_alloc_start = fs_info->alloc_start;
+       int old_thread_pool_size = fs_info->thread_pool_size;
+       unsigned int old_metadata_ratio = fs_info->metadata_ratio;
        int ret;
 
        ret = btrfs_parse_options(root, data);
-       if (ret)
-               return -EINVAL;
+       if (ret) {
+               ret = -EINVAL;
+               goto restore;
+       }
 
        if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
                return 0;
@@ -1007,26 +1144,44 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
        if (*flags & MS_RDONLY) {
                sb->s_flags |= MS_RDONLY;
 
-               ret =  btrfs_commit_super(root);
-               WARN_ON(ret);
+               ret = btrfs_commit_super(root);
+               if (ret)
+                       goto restore;
        } else {
                if (fs_info->fs_devices->rw_devices == 0)
-                       return -EACCES;
+                       ret = -EACCES;
+                       goto restore;
 
                if (btrfs_super_log_root(fs_info->super_copy) != 0)
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto restore;
 
                ret = btrfs_cleanup_fs_roots(fs_info);
-               WARN_ON(ret);
+               if (ret)
+                       goto restore;
 
                /* recover relocation */
                ret = btrfs_recover_relocation(root);
-               WARN_ON(ret);
+               if (ret)
+                       goto restore;
 
                sb->s_flags &= ~MS_RDONLY;
        }
 
        return 0;
+
+restore:
+       /* We've hit an error - don't reset MS_RDONLY */
+       if (sb->s_flags & MS_RDONLY)
+               old_flags |= MS_RDONLY;
+       sb->s_flags = old_flags;
+       fs_info->mount_opt = old_opts;
+       fs_info->compress_type = old_compress_type;
+       fs_info->max_inline = old_max_inline;
+       fs_info->alloc_start = old_alloc_start;
+       fs_info->thread_pool_size = old_thread_pool_size;
+       fs_info->metadata_ratio = old_metadata_ratio;
+       return ret;
 }
 
 /* Used to sort the devices by max_avail(descending sort) */
@@ -1356,9 +1511,7 @@ static int __init init_btrfs_fs(void)
        if (err)
                return err;
 
-       err = btrfs_init_compress();
-       if (err)
-               goto free_sysfs;
+       btrfs_init_compress();
 
        err = btrfs_init_cachep();
        if (err)
@@ -1384,6 +1537,8 @@ static int __init init_btrfs_fs(void)
        if (err)
                goto unregister_ioctl;
 
+       btrfs_init_lockdep();
+
        printk(KERN_INFO "%s loaded\n", BTRFS_BUILD_VERSION);
        return 0;
 
@@ -1399,7 +1554,6 @@ free_cachep:
        btrfs_destroy_cachep();
 free_compress:
        btrfs_exit_compress();
-free_sysfs:
        btrfs_exit_sysfs();
        return err;
 }
index 04b77e3ceb7a9783332c33c4ceca006ac9c14482..8da29e8e4de15999397f137825bb378c2e983ede 100644 (file)
@@ -31,7 +31,7 @@
 
 #define BTRFS_ROOT_TRANS_TAG 0
 
-static noinline void put_transaction(struct btrfs_transaction *transaction)
+void put_transaction(struct btrfs_transaction *transaction)
 {
        WARN_ON(atomic_read(&transaction->use_count) == 0);
        if (atomic_dec_and_test(&transaction->use_count)) {
@@ -58,6 +58,12 @@ static noinline int join_transaction(struct btrfs_root *root, int nofail)
 
        spin_lock(&root->fs_info->trans_lock);
 loop:
+       /* The file system has been taken offline. No new transactions. */
+       if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
+               spin_unlock(&root->fs_info->trans_lock);
+               return -EROFS;
+       }
+
        if (root->fs_info->trans_no_join) {
                if (!nofail) {
                        spin_unlock(&root->fs_info->trans_lock);
@@ -67,6 +73,8 @@ loop:
 
        cur_trans = root->fs_info->running_transaction;
        if (cur_trans) {
+               if (cur_trans->aborted)
+                       return cur_trans->aborted;
                atomic_inc(&cur_trans->use_count);
                atomic_inc(&cur_trans->num_writers);
                cur_trans->num_joined++;
@@ -123,6 +131,7 @@ loop:
        root->fs_info->generation++;
        cur_trans->transid = root->fs_info->generation;
        root->fs_info->running_transaction = cur_trans;
+       cur_trans->aborted = 0;
        spin_unlock(&root->fs_info->trans_lock);
 
        return 0;
@@ -318,6 +327,7 @@ again:
        h->use_count = 1;
        h->block_rsv = NULL;
        h->orig_rsv = NULL;
+       h->aborted = 0;
 
        smp_mb();
        if (cur_trans->blocked && may_wait_transaction(root, type)) {
@@ -327,8 +337,7 @@ again:
 
        if (num_bytes) {
                trace_btrfs_space_reservation(root->fs_info, "transaction",
-                                             (u64)(unsigned long)h,
-                                             num_bytes, 1);
+                                             h->transid, num_bytes, 1);
                h->block_rsv = &root->fs_info->trans_block_rsv;
                h->bytes_reserved = num_bytes;
        }
@@ -440,6 +449,7 @@ int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,
        struct btrfs_transaction *cur_trans = trans->transaction;
        struct btrfs_block_rsv *rsv = trans->block_rsv;
        int updates;
+       int err;
 
        smp_mb();
        if (cur_trans->blocked || cur_trans->delayed_refs.flushing)
@@ -453,8 +463,11 @@ int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,
 
        updates = trans->delayed_ref_updates;
        trans->delayed_ref_updates = 0;
-       if (updates)
-               btrfs_run_delayed_refs(trans, root, updates);
+       if (updates) {
+               err = btrfs_run_delayed_refs(trans, root, updates);
+               if (err) /* Error code will also eval true */
+                       return err;
+       }
 
        trans->block_rsv = rsv;
 
@@ -525,6 +538,11 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
        if (throttle)
                btrfs_run_delayed_iputs(root);
 
+       if (trans->aborted ||
+           root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
+               return -EIO;
+       }
+
        return 0;
 }
 
@@ -690,11 +708,13 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
                ret = btrfs_update_root(trans, tree_root,
                                        &root->root_key,
                                        &root->root_item);
-               BUG_ON(ret);
+               if (ret)
+                       return ret;
 
                old_root_used = btrfs_root_used(&root->root_item);
                ret = btrfs_write_dirty_block_groups(trans, root);
-               BUG_ON(ret);
+               if (ret)
+                       return ret;
        }
 
        if (root != root->fs_info->extent_root)
@@ -705,6 +725,10 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
 
 /*
  * update all the cowonly tree roots on disk
+ *
+ * The error handling in this function may not be obvious. Any of the
+ * failures will cause the file system to go offline. We still need
+ * to clean up the delayed refs.
  */
 static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
                                         struct btrfs_root *root)
@@ -715,22 +739,30 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
        int ret;
 
        ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
-       BUG_ON(ret);
+       if (ret)
+               return ret;
 
        eb = btrfs_lock_root_node(fs_info->tree_root);
-       btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb);
+       ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL,
+                             0, &eb);
        btrfs_tree_unlock(eb);
        free_extent_buffer(eb);
 
+       if (ret)
+               return ret;
+
        ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
-       BUG_ON(ret);
+       if (ret)
+               return ret;
 
        while (!list_empty(&fs_info->dirty_cowonly_roots)) {
                next = fs_info->dirty_cowonly_roots.next;
                list_del_init(next);
                root = list_entry(next, struct btrfs_root, dirty_list);
 
-               update_cowonly_root(trans, root);
+               ret = update_cowonly_root(trans, root);
+               if (ret)
+                       return ret;
        }
 
        down_write(&fs_info->extent_commit_sem);
@@ -874,7 +906,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
 
        new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS);
        if (!new_root_item) {
-               pending->error = -ENOMEM;
+               ret = pending->error = -ENOMEM;
                goto fail;
        }
 
@@ -911,21 +943,24 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
         * insert the directory item
         */
        ret = btrfs_set_inode_index(parent_inode, &index);
-       BUG_ON(ret);
+       BUG_ON(ret); /* -ENOMEM */
        ret = btrfs_insert_dir_item(trans, parent_root,
                                dentry->d_name.name, dentry->d_name.len,
                                parent_inode, &key,
                                BTRFS_FT_DIR, index);
-       if (ret) {
+       if (ret == -EEXIST) {
                pending->error = -EEXIST;
                dput(parent);
                goto fail;
+       } else if (ret) {
+               goto abort_trans_dput;
        }
 
        btrfs_i_size_write(parent_inode, parent_inode->i_size +
                                         dentry->d_name.len * 2);
        ret = btrfs_update_inode(trans, parent_root, parent_inode);
-       BUG_ON(ret);
+       if (ret)
+               goto abort_trans_dput;
 
        /*
         * pull in the delayed directory update
@@ -934,7 +969,10 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
         * snapshot
         */
        ret = btrfs_run_delayed_items(trans, root);
-       BUG_ON(ret);
+       if (ret) { /* Transaction aborted */
+               dput(parent);
+               goto fail;
+       }
 
        record_root_in_trans(trans, root);
        btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
@@ -949,12 +987,21 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
        btrfs_set_root_flags(new_root_item, root_flags);
 
        old = btrfs_lock_root_node(root);
-       btrfs_cow_block(trans, root, old, NULL, 0, &old);
+       ret = btrfs_cow_block(trans, root, old, NULL, 0, &old);
+       if (ret) {
+               btrfs_tree_unlock(old);
+               free_extent_buffer(old);
+               goto abort_trans_dput;
+       }
+
        btrfs_set_lock_blocking(old);
 
-       btrfs_copy_root(trans, root, old, &tmp, objectid);
+       ret = btrfs_copy_root(trans, root, old, &tmp, objectid);
+       /* clean up in any case */
        btrfs_tree_unlock(old);
        free_extent_buffer(old);
+       if (ret)
+               goto abort_trans_dput;
 
        /* see comments in should_cow_block() */
        root->force_cow = 1;
@@ -966,7 +1013,8 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
        ret = btrfs_insert_root(trans, tree_root, &key, new_root_item);
        btrfs_tree_unlock(tmp);
        free_extent_buffer(tmp);
-       BUG_ON(ret);
+       if (ret)
+               goto abort_trans_dput;
 
        /*
         * insert root back/forward references
@@ -975,19 +1023,32 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
                                 parent_root->root_key.objectid,
                                 btrfs_ino(parent_inode), index,
                                 dentry->d_name.name, dentry->d_name.len);
-       BUG_ON(ret);
        dput(parent);
+       if (ret)
+               goto fail;
 
        key.offset = (u64)-1;
        pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key);
-       BUG_ON(IS_ERR(pending->snap));
+       if (IS_ERR(pending->snap)) {
+               ret = PTR_ERR(pending->snap);
+               goto abort_trans;
+       }
 
-       btrfs_reloc_post_snapshot(trans, pending);
+       ret = btrfs_reloc_post_snapshot(trans, pending);
+       if (ret)
+               goto abort_trans;
+       ret = 0;
 fail:
        kfree(new_root_item);
        trans->block_rsv = rsv;
        btrfs_block_rsv_release(root, &pending->block_rsv, (u64)-1);
-       return 0;
+       return ret;
+
+abort_trans_dput:
+       dput(parent);
+abort_trans:
+       btrfs_abort_transaction(trans, root, ret);
+       goto fail;
 }
 
 /*
@@ -1124,6 +1185,33 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+
+static void cleanup_transaction(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *root)
+{
+       struct btrfs_transaction *cur_trans = trans->transaction;
+
+       WARN_ON(trans->use_count > 1);
+
+       spin_lock(&root->fs_info->trans_lock);
+       list_del_init(&cur_trans->list);
+       spin_unlock(&root->fs_info->trans_lock);
+
+       btrfs_cleanup_one_transaction(trans->transaction, root);
+
+       put_transaction(cur_trans);
+       put_transaction(cur_trans);
+
+       trace_btrfs_transaction_commit(root);
+
+       btrfs_scrub_continue(root);
+
+       if (current->journal_info == trans)
+               current->journal_info = NULL;
+
+       kmem_cache_free(btrfs_trans_handle_cachep, trans);
+}
+
 /*
  * btrfs_transaction state sequence:
  *    in_commit = 0, blocked = 0  (initial)
@@ -1135,10 +1223,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root)
 {
        unsigned long joined = 0;
-       struct btrfs_transaction *cur_trans;
+       struct btrfs_transaction *cur_trans = trans->transaction;
        struct btrfs_transaction *prev_trans = NULL;
        DEFINE_WAIT(wait);
-       int ret;
+       int ret = -EIO;
        int should_grow = 0;
        unsigned long now = get_seconds();
        int flush_on_commit = btrfs_test_opt(root, FLUSHONCOMMIT);
@@ -1148,13 +1236,18 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        btrfs_trans_release_metadata(trans, root);
        trans->block_rsv = NULL;
 
+       if (cur_trans->aborted)
+               goto cleanup_transaction;
+
        /* make a pass through all the delayed refs we have so far
         * any runnings procs may add more while we are here
         */
        ret = btrfs_run_delayed_refs(trans, root, 0);
-       BUG_ON(ret);
+       if (ret)
+               goto cleanup_transaction;
 
        cur_trans = trans->transaction;
+
        /*
         * set the flushing flag so procs in this transaction have to
         * start sending their work down.
@@ -1162,19 +1255,20 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        cur_trans->delayed_refs.flushing = 1;
 
        ret = btrfs_run_delayed_refs(trans, root, 0);
-       BUG_ON(ret);
+       if (ret)
+               goto cleanup_transaction;
 
        spin_lock(&cur_trans->commit_lock);
        if (cur_trans->in_commit) {
                spin_unlock(&cur_trans->commit_lock);
                atomic_inc(&cur_trans->use_count);
-               btrfs_end_transaction(trans, root);
+               ret = btrfs_end_transaction(trans, root);
 
                wait_for_commit(root, cur_trans);
 
                put_transaction(cur_trans);
 
-               return 0;
+               return ret;
        }
 
        trans->transaction->in_commit = 1;
@@ -1214,12 +1308,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
 
                if (flush_on_commit || snap_pending) {
                        btrfs_start_delalloc_inodes(root, 1);
-                       ret = btrfs_wait_ordered_extents(root, 0, 1);
-                       BUG_ON(ret);
+                       btrfs_wait_ordered_extents(root, 0, 1);
                }
 
                ret = btrfs_run_delayed_items(trans, root);
-               BUG_ON(ret);
+               if (ret)
+                       goto cleanup_transaction;
 
                /*
                 * rename don't use btrfs_join_transaction, so, once we
@@ -1261,13 +1355,22 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        mutex_lock(&root->fs_info->reloc_mutex);
 
        ret = btrfs_run_delayed_items(trans, root);
-       BUG_ON(ret);
+       if (ret) {
+               mutex_unlock(&root->fs_info->reloc_mutex);
+               goto cleanup_transaction;
+       }
 
        ret = create_pending_snapshots(trans, root->fs_info);
-       BUG_ON(ret);
+       if (ret) {
+               mutex_unlock(&root->fs_info->reloc_mutex);
+               goto cleanup_transaction;
+       }
 
        ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
-       BUG_ON(ret);
+       if (ret) {
+               mutex_unlock(&root->fs_info->reloc_mutex);
+               goto cleanup_transaction;
+       }
 
        /*
         * make sure none of the code above managed to slip in a
@@ -1294,7 +1397,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        mutex_lock(&root->fs_info->tree_log_mutex);
 
        ret = commit_fs_roots(trans, root);
-       BUG_ON(ret);
+       if (ret) {
+               mutex_unlock(&root->fs_info->tree_log_mutex);
+               goto cleanup_transaction;
+       }
 
        /* commit_fs_roots gets rid of all the tree log roots, it is now
         * safe to free the root of tree log roots
@@ -1302,7 +1408,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        btrfs_free_log_root_tree(trans, root->fs_info);
 
        ret = commit_cowonly_roots(trans, root);
-       BUG_ON(ret);
+       if (ret) {
+               mutex_unlock(&root->fs_info->tree_log_mutex);
+               goto cleanup_transaction;
+       }
 
        btrfs_prepare_extent_commit(trans, root);
 
@@ -1336,8 +1445,18 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        wake_up(&root->fs_info->transaction_wait);
 
        ret = btrfs_write_and_wait_transaction(trans, root);
-       BUG_ON(ret);
-       write_ctree_super(trans, root, 0);
+       if (ret) {
+               btrfs_error(root->fs_info, ret,
+                           "Error while writing out transaction.");
+               mutex_unlock(&root->fs_info->tree_log_mutex);
+               goto cleanup_transaction;
+       }
+
+       ret = write_ctree_super(trans, root, 0);
+       if (ret) {
+               mutex_unlock(&root->fs_info->tree_log_mutex);
+               goto cleanup_transaction;
+       }
 
        /*
         * the super is written, we can safely allow the tree-loggers
@@ -1373,6 +1492,15 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                btrfs_run_delayed_iputs(root);
 
        return ret;
+
+cleanup_transaction:
+       btrfs_printk(root->fs_info, "Skipping commit of aborted transaction.\n");
+//     WARN_ON(1);
+       if (current->journal_info == trans)
+               current->journal_info = NULL;
+       cleanup_transaction(trans, root);
+
+       return ret;
 }
 
 /*
@@ -1388,6 +1516,8 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root)
        spin_unlock(&fs_info->trans_lock);
 
        while (!list_empty(&list)) {
+               int ret;
+
                root = list_entry(list.next, struct btrfs_root, root_list);
                list_del(&root->root_list);
 
@@ -1395,9 +1525,10 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root)
 
                if (btrfs_header_backref_rev(root->node) <
                    BTRFS_MIXED_BACKREF_REV)
-                       btrfs_drop_snapshot(root, NULL, 0, 0);
+                       ret = btrfs_drop_snapshot(root, NULL, 0, 0);
                else
-                       btrfs_drop_snapshot(root, NULL, 1, 0);
+                       ret =btrfs_drop_snapshot(root, NULL, 1, 0);
+               BUG_ON(ret < 0);
        }
        return 0;
 }
index 02564e6230acd672b5fa6539cc88890417204e41..fe27379e368bf43c53927d37fa1c32a4f67f05d0 100644 (file)
@@ -43,6 +43,7 @@ struct btrfs_transaction {
        wait_queue_head_t commit_wait;
        struct list_head pending_snapshots;
        struct btrfs_delayed_ref_root delayed_refs;
+       int aborted;
 };
 
 struct btrfs_trans_handle {
@@ -55,6 +56,7 @@ struct btrfs_trans_handle {
        struct btrfs_transaction *transaction;
        struct btrfs_block_rsv *block_rsv;
        struct btrfs_block_rsv *orig_rsv;
+       int aborted;
 };
 
 struct btrfs_pending_snapshot {
@@ -114,4 +116,5 @@ int btrfs_wait_marked_extents(struct btrfs_root *root,
                                struct extent_io_tree *dirty_pages, int mark);
 int btrfs_transaction_blocked(struct btrfs_fs_info *info);
 int btrfs_transaction_in_commit(struct btrfs_fs_info *info);
+void put_transaction(struct btrfs_transaction *transaction);
 #endif
index 966cc74f5d6c7303b06bb8bd067826f07510ffc8..d017283ae6f56fa8ea0ba1eeaa04077d443eb0fb 100644 (file)
@@ -212,14 +212,13 @@ int btrfs_pin_log_trans(struct btrfs_root *root)
  * indicate we're done making changes to the log tree
  * and wake up anyone waiting to do a sync
  */
-int btrfs_end_log_trans(struct btrfs_root *root)
+void btrfs_end_log_trans(struct btrfs_root *root)
 {
        if (atomic_dec_and_test(&root->log_writers)) {
                smp_mb();
                if (waitqueue_active(&root->log_writer_wait))
                        wake_up(&root->log_writer_wait);
        }
-       return 0;
 }
 
 
@@ -378,12 +377,11 @@ insert:
                u32 found_size;
                found_size = btrfs_item_size_nr(path->nodes[0],
                                                path->slots[0]);
-               if (found_size > item_size) {
+               if (found_size > item_size)
                        btrfs_truncate_item(trans, root, path, item_size, 1);
-               } else if (found_size < item_size) {
-                       ret = btrfs_extend_item(trans, root, path,
-                                               item_size - found_size);
-               }
+               else if (found_size < item_size)
+                       btrfs_extend_item(trans, root, path,
+                                         item_size - found_size);
        } else if (ret) {
                return ret;
        }
@@ -1763,7 +1761,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                                        BTRFS_TREE_LOG_OBJECTID);
                                ret = btrfs_free_and_pin_reserved_extent(root,
                                                         bytenr, blocksize);
-                               BUG_ON(ret);
+                               BUG_ON(ret); /* -ENOMEM or logic errors */
                        }
                        free_extent_buffer(next);
                        continue;
@@ -1871,20 +1869,26 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
                wret = walk_down_log_tree(trans, log, path, &level, wc);
                if (wret > 0)
                        break;
-               if (wret < 0)
+               if (wret < 0) {
                        ret = wret;
+                       goto out;
+               }
 
                wret = walk_up_log_tree(trans, log, path, &level, wc);
                if (wret > 0)
                        break;
-               if (wret < 0)
+               if (wret < 0) {
                        ret = wret;
+                       goto out;
+               }
        }
 
        /* was the root node processed? if not, catch it here */
        if (path->nodes[orig_level]) {
-               wc->process_func(log, path->nodes[orig_level], wc,
+               ret = wc->process_func(log, path->nodes[orig_level], wc,
                         btrfs_header_generation(path->nodes[orig_level]));
+               if (ret)
+                       goto out;
                if (wc->free) {
                        struct extent_buffer *next;
 
@@ -1900,10 +1904,11 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
                                BTRFS_TREE_LOG_OBJECTID);
                        ret = btrfs_free_and_pin_reserved_extent(log, next->start,
                                                         next->len);
-                       BUG_ON(ret);
+                       BUG_ON(ret); /* -ENOMEM or logic errors */
                }
        }
 
+out:
        for (i = 0; i <= orig_level; i++) {
                if (path->nodes[i]) {
                        free_extent_buffer(path->nodes[i]);
@@ -1963,8 +1968,8 @@ static int wait_log_commit(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-static int wait_for_writer(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root)
+static void wait_for_writer(struct btrfs_trans_handle *trans,
+                           struct btrfs_root *root)
 {
        DEFINE_WAIT(wait);
        while (root->fs_info->last_trans_log_full_commit !=
@@ -1978,7 +1983,6 @@ static int wait_for_writer(struct btrfs_trans_handle *trans,
                mutex_lock(&root->log_mutex);
                finish_wait(&root->log_writer_wait, &wait);
        }
-       return 0;
 }
 
 /*
@@ -2046,7 +2050,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
         * wait for them until later.
         */
        ret = btrfs_write_marked_extents(log, &log->dirty_log_pages, mark);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               mutex_unlock(&root->log_mutex);
+               goto out;
+       }
 
        btrfs_set_root_node(&log->root_item, log->node);
 
@@ -2077,7 +2085,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        }
 
        if (ret) {
-               BUG_ON(ret != -ENOSPC);
+               if (ret != -ENOSPC) {
+                       btrfs_abort_transaction(trans, root, ret);
+                       mutex_unlock(&log_root_tree->log_mutex);
+                       goto out;
+               }
                root->fs_info->last_trans_log_full_commit = trans->transid;
                btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
                mutex_unlock(&log_root_tree->log_mutex);
@@ -2117,7 +2129,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        ret = btrfs_write_and_wait_marked_extents(log_root_tree,
                                &log_root_tree->dirty_log_pages,
                                EXTENT_DIRTY | EXTENT_NEW);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               mutex_unlock(&log_root_tree->log_mutex);
+               goto out_wake_log_root;
+       }
        btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
 
        btrfs_set_super_log_root(root->fs_info->super_for_commit,
@@ -2326,7 +2342,9 @@ out_unlock:
        if (ret == -ENOSPC) {
                root->fs_info->last_trans_log_full_commit = trans->transid;
                ret = 0;
-       }
+       } else if (ret < 0)
+               btrfs_abort_transaction(trans, root, ret);
+
        btrfs_end_log_trans(root);
 
        return err;
@@ -2357,7 +2375,8 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
        if (ret == -ENOSPC) {
                root->fs_info->last_trans_log_full_commit = trans->transid;
                ret = 0;
-       }
+       } else if (ret < 0 && ret != -ENOENT)
+               btrfs_abort_transaction(trans, root, ret);
        btrfs_end_log_trans(root);
 
        return ret;
@@ -3169,13 +3188,20 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
        fs_info->log_root_recovering = 1;
 
        trans = btrfs_start_transaction(fs_info->tree_root, 0);
-       BUG_ON(IS_ERR(trans));
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               goto error;
+       }
 
        wc.trans = trans;
        wc.pin = 1;
 
        ret = walk_log_tree(trans, log_root_tree, &wc);
-       BUG_ON(ret);
+       if (ret) {
+               btrfs_error(fs_info, ret, "Failed to pin buffers while "
+                           "recovering log root tree.");
+               goto error;
+       }
 
 again:
        key.objectid = BTRFS_TREE_LOG_OBJECTID;
@@ -3184,8 +3210,12 @@ again:
 
        while (1) {
                ret = btrfs_search_slot(NULL, log_root_tree, &key, path, 0, 0);
-               if (ret < 0)
-                       break;
+
+               if (ret < 0) {
+                       btrfs_error(fs_info, ret,
+                                   "Couldn't find tree log root.");
+                       goto error;
+               }
                if (ret > 0) {
                        if (path->slots[0] == 0)
                                break;
@@ -3199,14 +3229,24 @@ again:
 
                log = btrfs_read_fs_root_no_radix(log_root_tree,
                                                  &found_key);
-               BUG_ON(IS_ERR(log));
+               if (IS_ERR(log)) {
+                       ret = PTR_ERR(log);
+                       btrfs_error(fs_info, ret,
+                                   "Couldn't read tree log root.");
+                       goto error;
+               }
 
                tmp_key.objectid = found_key.offset;
                tmp_key.type = BTRFS_ROOT_ITEM_KEY;
                tmp_key.offset = (u64)-1;
 
                wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key);
-               BUG_ON(IS_ERR_OR_NULL(wc.replay_dest));
+               if (IS_ERR(wc.replay_dest)) {
+                       ret = PTR_ERR(wc.replay_dest);
+                       btrfs_error(fs_info, ret, "Couldn't read target root "
+                                   "for tree log recovery.");
+                       goto error;
+               }
 
                wc.replay_dest->log_root = log;
                btrfs_record_root_in_trans(trans, wc.replay_dest);
@@ -3254,6 +3294,10 @@ again:
 
        kfree(log_root_tree);
        return 0;
+
+error:
+       btrfs_free_path(path);
+       return ret;
 }
 
 /*
index 2270ac58d7469849c748de5bf55aa96ece38f7a2..862ac813f6b87cbc9988a043c82253cfff6ca030 100644 (file)
@@ -38,7 +38,7 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
                               struct btrfs_root *root,
                               const char *name, int name_len,
                               struct inode *inode, u64 dirid);
-int btrfs_end_log_trans(struct btrfs_root *root);
+void btrfs_end_log_trans(struct btrfs_root *root);
 int btrfs_pin_log_trans(struct btrfs_root *root);
 int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
                    struct btrfs_root *root, struct inode *inode,
index ef41f285a47542bdd0e8f94aec56e18e7d833fba..a872b48be0ae15fd77eff56e2c529470ddde0b7f 100644 (file)
@@ -67,7 +67,7 @@ static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
        kfree(fs_devices);
 }
 
-int btrfs_cleanup_fs_uuids(void)
+void btrfs_cleanup_fs_uuids(void)
 {
        struct btrfs_fs_devices *fs_devices;
 
@@ -77,7 +77,6 @@ int btrfs_cleanup_fs_uuids(void)
                list_del(&fs_devices->list);
                free_fs_devices(fs_devices);
        }
-       return 0;
 }
 
 static noinline struct btrfs_device *__find_device(struct list_head *head,
@@ -130,7 +129,7 @@ static void requeue_list(struct btrfs_pending_bios *pending_bios,
  * the list if the block device is congested.  This way, multiple devices
  * can make progress from a single worker thread.
  */
-static noinline int run_scheduled_bios(struct btrfs_device *device)
+static noinline void run_scheduled_bios(struct btrfs_device *device)
 {
        struct bio *pending;
        struct backing_dev_info *bdi;
@@ -316,7 +315,6 @@ loop_lock:
 
 done:
        blk_finish_plug(&plug);
-       return 0;
 }
 
 static void pending_bios_fn(struct btrfs_work *work)
@@ -455,7 +453,7 @@ error:
        return ERR_PTR(-ENOMEM);
 }
 
-int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
+void btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
 {
        struct btrfs_device *device, *next;
 
@@ -503,7 +501,6 @@ again:
        fs_devices->latest_trans = latest_transid;
 
        mutex_unlock(&uuid_mutex);
-       return 0;
 }
 
 static void __free_device(struct work_struct *work)
@@ -552,10 +549,10 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
                        fs_devices->num_can_discard--;
 
                new_device = kmalloc(sizeof(*new_device), GFP_NOFS);
-               BUG_ON(!new_device);
+               BUG_ON(!new_device); /* -ENOMEM */
                memcpy(new_device, device, sizeof(*new_device));
                new_device->name = kstrdup(device->name, GFP_NOFS);
-               BUG_ON(device->name && !new_device->name);
+               BUG_ON(device->name && !new_device->name); /* -ENOMEM */
                new_device->bdev = NULL;
                new_device->writeable = 0;
                new_device->in_fs_metadata = 0;
@@ -625,6 +622,8 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
                        printk(KERN_INFO "open %s failed\n", device->name);
                        goto error;
                }
+               filemap_write_and_wait(bdev->bd_inode->i_mapping);
+               invalidate_bdev(bdev);
                set_blocksize(bdev, 4096);
 
                bh = btrfs_read_dev_super(bdev);
@@ -1039,8 +1038,10 @@ again:
                leaf = path->nodes[0];
                extent = btrfs_item_ptr(leaf, path->slots[0],
                                        struct btrfs_dev_extent);
+       } else {
+               btrfs_error(root->fs_info, ret, "Slot search failed");
+               goto out;
        }
-       BUG_ON(ret);
 
        if (device->bytes_used > 0) {
                u64 len = btrfs_dev_extent_length(leaf, extent);
@@ -1050,7 +1051,10 @@ again:
                spin_unlock(&root->fs_info->free_chunk_lock);
        }
        ret = btrfs_del_item(trans, root, path);
-
+       if (ret) {
+               btrfs_error(root->fs_info, ret,
+                           "Failed to remove dev extent item");
+       }
 out:
        btrfs_free_path(path);
        return ret;
@@ -1078,7 +1082,8 @@ int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
        key.type = BTRFS_DEV_EXTENT_KEY;
        ret = btrfs_insert_empty_item(trans, root, path, &key,
                                      sizeof(*extent));
-       BUG_ON(ret);
+       if (ret)
+               goto out;
 
        leaf = path->nodes[0];
        extent = btrfs_item_ptr(leaf, path->slots[0],
@@ -1093,6 +1098,7 @@ int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
 
        btrfs_set_dev_extent_length(leaf, extent, num_bytes);
        btrfs_mark_buffer_dirty(leaf);
+out:
        btrfs_free_path(path);
        return ret;
 }
@@ -1118,7 +1124,7 @@ static noinline int find_next_chunk(struct btrfs_root *root,
        if (ret < 0)
                goto error;
 
-       BUG_ON(ret == 0);
+       BUG_ON(ret == 0); /* Corruption */
 
        ret = btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY);
        if (ret) {
@@ -1162,7 +1168,7 @@ static noinline int find_next_devid(struct btrfs_root *root, u64 *objectid)
        if (ret < 0)
                goto error;
 
-       BUG_ON(ret == 0);
+       BUG_ON(ret == 0); /* Corruption */
 
        ret = btrfs_previous_item(root, path, BTRFS_DEV_ITEMS_OBJECTID,
                                  BTRFS_DEV_ITEM_KEY);
@@ -1350,6 +1356,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
                }
 
                set_blocksize(bdev, 4096);
+               invalidate_bdev(bdev);
                bh = btrfs_read_dev_super(bdev);
                if (!bh) {
                        ret = -EINVAL;
@@ -1596,7 +1603,7 @@ next_slot:
                                   (unsigned long)btrfs_device_fsid(dev_item),
                                   BTRFS_UUID_SIZE);
                device = btrfs_find_device(root, devid, dev_uuid, fs_uuid);
-               BUG_ON(!device);
+               BUG_ON(!device); /* Logic error */
 
                if (device->fs_devices->seeding) {
                        btrfs_set_device_generation(leaf, dev_item,
@@ -1706,7 +1713,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        if (seeding_dev) {
                sb->s_flags &= ~MS_RDONLY;
                ret = btrfs_prepare_sprout(root);
-               BUG_ON(ret);
+               BUG_ON(ret); /* -ENOMEM */
        }
 
        device->fs_devices = root->fs_info->fs_devices;
@@ -1744,11 +1751,15 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
 
        if (seeding_dev) {
                ret = init_first_rw_device(trans, root, device);
-               BUG_ON(ret);
+               if (ret)
+                       goto error_trans;
                ret = btrfs_finish_sprout(trans, root);
-               BUG_ON(ret);
+               if (ret)
+                       goto error_trans;
        } else {
                ret = btrfs_add_device(trans, root, device);
+               if (ret)
+                       goto error_trans;
        }
 
        /*
@@ -1758,17 +1769,31 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        btrfs_clear_space_info_full(root->fs_info);
 
        unlock_chunks(root);
-       btrfs_commit_transaction(trans, root);
+       ret = btrfs_commit_transaction(trans, root);
 
        if (seeding_dev) {
                mutex_unlock(&uuid_mutex);
                up_write(&sb->s_umount);
 
+               if (ret) /* transaction commit */
+                       return ret;
+
                ret = btrfs_relocate_sys_chunks(root);
-               BUG_ON(ret);
+               if (ret < 0)
+                       btrfs_error(root->fs_info, ret,
+                                   "Failed to relocate sys chunks after "
+                                   "device initialization. This can be fixed "
+                                   "using the \"btrfs balance\" command.");
        }
 
        return ret;
+
+error_trans:
+       unlock_chunks(root);
+       btrfs_abort_transaction(trans, root, ret);
+       btrfs_end_transaction(trans, root);
+       kfree(device->name);
+       kfree(device);
 error:
        blkdev_put(bdev, FMODE_EXCL);
        if (seeding_dev) {
@@ -1876,10 +1901,20 @@ static int btrfs_free_chunk(struct btrfs_trans_handle *trans,
        key.type = BTRFS_CHUNK_ITEM_KEY;
 
        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
-       BUG_ON(ret);
+       if (ret < 0)
+               goto out;
+       else if (ret > 0) { /* Logic error or corruption */
+               btrfs_error(root->fs_info, -ENOENT,
+                           "Failed lookup while freeing chunk.");
+               ret = -ENOENT;
+               goto out;
+       }
 
        ret = btrfs_del_item(trans, root, path);
-
+       if (ret < 0)
+               btrfs_error(root->fs_info, ret,
+                           "Failed to delete chunk item.");
+out:
        btrfs_free_path(path);
        return ret;
 }
@@ -2041,7 +2076,7 @@ again:
                ret = btrfs_search_slot(NULL, chunk_root, &key, path, 0, 0);
                if (ret < 0)
                        goto error;
-               BUG_ON(ret == 0);
+               BUG_ON(ret == 0); /* Corruption */
 
                ret = btrfs_previous_item(chunk_root, path, key.objectid,
                                          key.type);
@@ -2250,15 +2285,13 @@ static void unset_balance_control(struct btrfs_fs_info *fs_info)
  * Balance filters.  Return 1 if chunk should be filtered out
  * (should not be balanced).
  */
-static int chunk_profiles_filter(u64 chunk_profile,
+static int chunk_profiles_filter(u64 chunk_type,
                                 struct btrfs_balance_args *bargs)
 {
-       chunk_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
-
-       if (chunk_profile == 0)
-               chunk_profile = BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+       chunk_type = chunk_to_extended(chunk_type) &
+                               BTRFS_EXTENDED_PROFILE_MASK;
 
-       if (bargs->profiles & chunk_profile)
+       if (bargs->profiles & chunk_type)
                return 0;
 
        return 1;
@@ -2365,18 +2398,16 @@ static int chunk_vrange_filter(struct extent_buffer *leaf,
        return 1;
 }
 
-static int chunk_soft_convert_filter(u64 chunk_profile,
+static int chunk_soft_convert_filter(u64 chunk_type,
                                     struct btrfs_balance_args *bargs)
 {
        if (!(bargs->flags & BTRFS_BALANCE_ARGS_CONVERT))
                return 0;
 
-       chunk_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
+       chunk_type = chunk_to_extended(chunk_type) &
+                               BTRFS_EXTENDED_PROFILE_MASK;
 
-       if (chunk_profile == 0)
-               chunk_profile = BTRFS_AVAIL_ALLOC_BIT_SINGLE;
-
-       if (bargs->target & chunk_profile)
+       if (bargs->target == chunk_type)
                return 1;
 
        return 0;
@@ -2602,6 +2633,30 @@ error:
        return ret;
 }
 
+/**
+ * alloc_profile_is_valid - see if a given profile is valid and reduced
+ * @flags: profile to validate
+ * @extended: if true @flags is treated as an extended profile
+ */
+static int alloc_profile_is_valid(u64 flags, int extended)
+{
+       u64 mask = (extended ? BTRFS_EXTENDED_PROFILE_MASK :
+                              BTRFS_BLOCK_GROUP_PROFILE_MASK);
+
+       flags &= ~BTRFS_BLOCK_GROUP_TYPE_MASK;
+
+       /* 1) check that all other bits are zeroed */
+       if (flags & ~mask)
+               return 0;
+
+       /* 2) see if profile is reduced */
+       if (flags == 0)
+               return !extended; /* "0" is valid for usual profiles */
+
+       /* true if exactly one bit set */
+       return (flags & (flags - 1)) == 0;
+}
+
 static inline int balance_need_close(struct btrfs_fs_info *fs_info)
 {
        /* cancel requested || normal exit path */
@@ -2630,6 +2685,7 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
 {
        struct btrfs_fs_info *fs_info = bctl->fs_info;
        u64 allowed;
+       int mixed = 0;
        int ret;
 
        if (btrfs_fs_closing(fs_info) ||
@@ -2639,13 +2695,16 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
                goto out;
        }
 
+       allowed = btrfs_super_incompat_flags(fs_info->super_copy);
+       if (allowed & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
+               mixed = 1;
+
        /*
         * In case of mixed groups both data and meta should be picked,
         * and identical options should be given for both of them.
         */
-       allowed = btrfs_super_incompat_flags(fs_info->super_copy);
-       if ((allowed & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS) &&
-           (bctl->flags & (BTRFS_BALANCE_DATA | BTRFS_BALANCE_METADATA))) {
+       allowed = BTRFS_BALANCE_DATA | BTRFS_BALANCE_METADATA;
+       if (mixed && (bctl->flags & allowed)) {
                if (!(bctl->flags & BTRFS_BALANCE_DATA) ||
                    !(bctl->flags & BTRFS_BALANCE_METADATA) ||
                    memcmp(&bctl->data, &bctl->meta, sizeof(bctl->data))) {
@@ -2656,14 +2715,6 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
                }
        }
 
-       /*
-        * Profile changing sanity checks.  Skip them if a simple
-        * balance is requested.
-        */
-       if (!((bctl->data.flags | bctl->sys.flags | bctl->meta.flags) &
-             BTRFS_BALANCE_ARGS_CONVERT))
-               goto do_balance;
-
        allowed = BTRFS_AVAIL_ALLOC_BIT_SINGLE;
        if (fs_info->fs_devices->num_devices == 1)
                allowed |= BTRFS_BLOCK_GROUP_DUP;
@@ -2673,24 +2724,27 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
                allowed |= (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 |
                                BTRFS_BLOCK_GROUP_RAID10);
 
-       if (!profile_is_valid(bctl->data.target, 1) ||
-           bctl->data.target & ~allowed) {
+       if ((bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
+           (!alloc_profile_is_valid(bctl->data.target, 1) ||
+            (bctl->data.target & ~allowed))) {
                printk(KERN_ERR "btrfs: unable to start balance with target "
                       "data profile %llu\n",
                       (unsigned long long)bctl->data.target);
                ret = -EINVAL;
                goto out;
        }
-       if (!profile_is_valid(bctl->meta.target, 1) ||
-           bctl->meta.target & ~allowed) {
+       if ((bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
+           (!alloc_profile_is_valid(bctl->meta.target, 1) ||
+            (bctl->meta.target & ~allowed))) {
                printk(KERN_ERR "btrfs: unable to start balance with target "
                       "metadata profile %llu\n",
                       (unsigned long long)bctl->meta.target);
                ret = -EINVAL;
                goto out;
        }
-       if (!profile_is_valid(bctl->sys.target, 1) ||
-           bctl->sys.target & ~allowed) {
+       if ((bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
+           (!alloc_profile_is_valid(bctl->sys.target, 1) ||
+            (bctl->sys.target & ~allowed))) {
                printk(KERN_ERR "btrfs: unable to start balance with target "
                       "system profile %llu\n",
                       (unsigned long long)bctl->sys.target);
@@ -2698,7 +2752,9 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
                goto out;
        }
 
-       if (bctl->data.target & BTRFS_BLOCK_GROUP_DUP) {
+       /* allow dup'ed data chunks only in mixed mode */
+       if (!mixed && (bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
+           (bctl->data.target & BTRFS_BLOCK_GROUP_DUP)) {
                printk(KERN_ERR "btrfs: dup for data is not allowed\n");
                ret = -EINVAL;
                goto out;
@@ -2724,7 +2780,6 @@ int btrfs_balance(struct btrfs_balance_control *bctl,
                }
        }
 
-do_balance:
        ret = insert_balance_item(fs_info->tree_root, bctl);
        if (ret && ret != -EEXIST)
                goto out;
@@ -2967,7 +3022,7 @@ again:
        key.offset = (u64)-1;
        key.type = BTRFS_DEV_EXTENT_KEY;
 
-       while (1) {
+       do {
                ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
                if (ret < 0)
                        goto done;
@@ -3009,8 +3064,7 @@ again:
                        goto done;
                if (ret == -ENOSPC)
                        failed++;
-               key.offset -= 1;
-       }
+       } while (key.offset-- > 0);
 
        if (failed && !retried) {
                failed = 0;
@@ -3128,11 +3182,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
        int i;
        int j;
 
-       if ((type & BTRFS_BLOCK_GROUP_RAID1) &&
-           (type & BTRFS_BLOCK_GROUP_DUP)) {
-               WARN_ON(1);
-               type &= ~BTRFS_BLOCK_GROUP_DUP;
-       }
+       BUG_ON(!alloc_profile_is_valid(type, 0));
 
        if (list_empty(&fs_devices->alloc_list))
                return -ENOSPC;
@@ -3328,13 +3378,15 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
        write_lock(&em_tree->lock);
        ret = add_extent_mapping(em_tree, em);
        write_unlock(&em_tree->lock);
-       BUG_ON(ret);
        free_extent_map(em);
+       if (ret)
+               goto error;
 
        ret = btrfs_make_block_group(trans, extent_root, 0, type,
                                     BTRFS_FIRST_CHUNK_TREE_OBJECTID,
                                     start, num_bytes);
-       BUG_ON(ret);
+       if (ret)
+               goto error;
 
        for (i = 0; i < map->num_stripes; ++i) {
                struct btrfs_device *device;
@@ -3347,7 +3399,10 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
                                info->chunk_root->root_key.objectid,
                                BTRFS_FIRST_CHUNK_TREE_OBJECTID,
                                start, dev_offset, stripe_size);
-               BUG_ON(ret);
+               if (ret) {
+                       btrfs_abort_transaction(trans, extent_root, ret);
+                       goto error;
+               }
        }
 
        kfree(devices_info);
@@ -3383,7 +3438,8 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans,
                device = map->stripes[index].dev;
                device->bytes_used += stripe_size;
                ret = btrfs_update_device(trans, device);
-               BUG_ON(ret);
+               if (ret)
+                       goto out_free;
                index++;
        }
 
@@ -3420,16 +3476,19 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans,
        key.offset = chunk_offset;
 
        ret = btrfs_insert_item(trans, chunk_root, &key, chunk, item_size);
-       BUG_ON(ret);
 
-       if (map->type & BTRFS_BLOCK_GROUP_SYSTEM) {
+       if (ret == 0 && map->type & BTRFS_BLOCK_GROUP_SYSTEM) {
+               /*
+                * TODO: Cleanup of inserted chunk root in case of
+                * failure.
+                */
                ret = btrfs_add_system_chunk(chunk_root, &key, chunk,
                                             item_size);
-               BUG_ON(ret);
        }
 
+out_free:
        kfree(chunk);
-       return 0;
+       return ret;
 }
 
 /*
@@ -3461,7 +3520,8 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
 
        ret = __finish_chunk_alloc(trans, extent_root, map, chunk_offset,
                                   chunk_size, stripe_size);
-       BUG_ON(ret);
+       if (ret)
+               return ret;
        return 0;
 }
 
@@ -3493,7 +3553,8 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
 
        ret = __btrfs_alloc_chunk(trans, extent_root, &map, &chunk_size,
                                  &stripe_size, chunk_offset, alloc_profile);
-       BUG_ON(ret);
+       if (ret)
+               return ret;
 
        sys_chunk_offset = chunk_offset + chunk_size;
 
@@ -3504,10 +3565,12 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
        ret = __btrfs_alloc_chunk(trans, extent_root, &sys_map,
                                  &sys_chunk_size, &sys_stripe_size,
                                  sys_chunk_offset, alloc_profile);
-       BUG_ON(ret);
+       if (ret)
+               goto abort;
 
        ret = btrfs_add_device(trans, fs_info->chunk_root, device);
-       BUG_ON(ret);
+       if (ret)
+               goto abort;
 
        /*
         * Modifying chunk tree needs allocating new blocks from both
@@ -3517,13 +3580,20 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
         */
        ret = __finish_chunk_alloc(trans, extent_root, map, chunk_offset,
                                   chunk_size, stripe_size);
-       BUG_ON(ret);
+       if (ret)
+               goto abort;
 
        ret = __finish_chunk_alloc(trans, extent_root, sys_map,
                                   sys_chunk_offset, sys_chunk_size,
                                   sys_stripe_size);
-       BUG_ON(ret);
+       if (ret)
+               goto abort;
+
        return 0;
+
+abort:
+       btrfs_abort_transaction(trans, root, ret);
+       return ret;
 }
 
 int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset)
@@ -3874,7 +3944,7 @@ int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
                do_div(length, map->num_stripes);
 
        buf = kzalloc(sizeof(u64) * map->num_stripes, GFP_NOFS);
-       BUG_ON(!buf);
+       BUG_ON(!buf); /* -ENOMEM */
 
        for (i = 0; i < map->num_stripes; i++) {
                if (devid && map->stripes[i].dev->devid != devid)
@@ -3967,7 +4037,7 @@ struct async_sched {
  * This will add one bio to the pending list for a device and make sure
  * the work struct is scheduled.
  */
-static noinline int schedule_bio(struct btrfs_root *root,
+static noinline void schedule_bio(struct btrfs_root *root,
                                 struct btrfs_device *device,
                                 int rw, struct bio *bio)
 {
@@ -3979,7 +4049,7 @@ static noinline int schedule_bio(struct btrfs_root *root,
                bio_get(bio);
                btrfsic_submit_bio(rw, bio);
                bio_put(bio);
-               return 0;
+               return;
        }
 
        /*
@@ -4013,7 +4083,6 @@ static noinline int schedule_bio(struct btrfs_root *root,
        if (should_queue)
                btrfs_queue_worker(&root->fs_info->submit_workers,
                                   &device->work);
-       return 0;
 }
 
 int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
@@ -4036,7 +4105,8 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
 
        ret = btrfs_map_block(map_tree, rw, logical, &map_length, &bbio,
                              mirror_num);
-       BUG_ON(ret);
+       if (ret) /* -ENOMEM */
+               return ret;
 
        total_devs = bbio->num_stripes;
        if (map_length < length) {
@@ -4055,7 +4125,7 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
        while (dev_nr < total_devs) {
                if (dev_nr < total_devs - 1) {
                        bio = bio_clone(first_bio, GFP_NOFS);
-                       BUG_ON(!bio);
+                       BUG_ON(!bio); /* -ENOMEM */
                } else {
                        bio = first_bio;
                }
@@ -4209,13 +4279,13 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
        write_lock(&map_tree->map_tree.lock);
        ret = add_extent_mapping(&map_tree->map_tree, em);
        write_unlock(&map_tree->map_tree.lock);
-       BUG_ON(ret);
+       BUG_ON(ret); /* Tree corruption */
        free_extent_map(em);
 
        return 0;
 }
 
-static int fill_device_from_item(struct extent_buffer *leaf,
+static void fill_device_from_item(struct extent_buffer *leaf,
                                 struct btrfs_dev_item *dev_item,
                                 struct btrfs_device *device)
 {
@@ -4232,8 +4302,6 @@ static int fill_device_from_item(struct extent_buffer *leaf,
 
        ptr = (unsigned long)btrfs_device_uuid(dev_item);
        read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
-
-       return 0;
 }
 
 static int open_seed_devices(struct btrfs_root *root, u8 *fsid)
@@ -4384,7 +4452,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
         * to silence the warning eg. on PowerPC 64.
         */
        if (PAGE_CACHE_SIZE > BTRFS_SUPER_INFO_SIZE)
-               SetPageUptodate(sb->first_page);
+               SetPageUptodate(sb->pages[0]);
 
        write_extent_buffer(sb, super_copy, 0, BTRFS_SUPER_INFO_SIZE);
        array_size = btrfs_super_sys_array_size(super_copy);
index 19ac95048b88596e44b6dd667b050fb796ab20e7..bb6b03f97aaa089793d667fae93335373773a7eb 100644 (file)
@@ -260,12 +260,12 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
 int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
                          struct btrfs_fs_devices **fs_devices_ret);
 int btrfs_close_devices(struct btrfs_fs_devices *fs_devices);
-int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices);
+void btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices);
 int btrfs_add_device(struct btrfs_trans_handle *trans,
                     struct btrfs_root *root,
                     struct btrfs_device *device);
 int btrfs_rm_device(struct btrfs_root *root, char *device_path);
-int btrfs_cleanup_fs_uuids(void);
+void btrfs_cleanup_fs_uuids(void);
 int btrfs_num_copies(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len);
 int btrfs_grow_device(struct btrfs_trans_handle *trans,
                      struct btrfs_device *device, u64 new_size);
index 573b899b5a5dfc55a4f6ab0f0e6dc679beacd6a2..2704646294166bec7edf9c006c98d5229a1eaab9 100644 (file)
@@ -58,15 +58,16 @@ cifs_dump_mem(char *label, void *data, int length)
 }
 
 #ifdef CONFIG_CIFS_DEBUG2
-void cifs_dump_detail(struct smb_hdr *smb)
+void cifs_dump_detail(void *buf)
 {
+       struct smb_hdr *smb = (struct smb_hdr *)buf;
+
        cERROR(1, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d",
                  smb->Command, smb->Status.CifsError,
                  smb->Flags, smb->Flags2, smb->Mid, smb->Pid);
        cERROR(1, "smb buf %p len %d", smb, smbCalcSize(smb));
 }
 
-
 void cifs_dump_mids(struct TCP_Server_Info *server)
 {
        struct list_head *tmp;
@@ -79,15 +80,15 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
        spin_lock(&GlobalMid_Lock);
        list_for_each(tmp, &server->pending_mid_q) {
                mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
-               cERROR(1, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %d",
-                       mid_entry->midState,
-                       (int)mid_entry->command,
+               cERROR(1, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu",
+                       mid_entry->mid_state,
+                       le16_to_cpu(mid_entry->command),
                        mid_entry->pid,
                        mid_entry->callback_data,
                        mid_entry->mid);
 #ifdef CONFIG_CIFS_STATS2
                cERROR(1, "IsLarge: %d buf: %p time rcv: %ld now: %ld",
-                       mid_entry->largeBuf,
+                       mid_entry->large_buf,
                        mid_entry->resp_buf,
                        mid_entry->when_received,
                        jiffies);
@@ -217,12 +218,12 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
                                mid_entry = list_entry(tmp3, struct mid_q_entry,
                                        qhead);
                                seq_printf(m, "\tState: %d com: %d pid:"
-                                               " %d cbdata: %p mid %d\n",
-                                               mid_entry->midState,
-                                               (int)mid_entry->command,
-                                               mid_entry->pid,
-                                               mid_entry->callback_data,
-                                               mid_entry->mid);
+                                             " %d cbdata: %p mid %llu\n",
+                                             mid_entry->mid_state,
+                                             le16_to_cpu(mid_entry->command),
+                                             mid_entry->pid,
+                                             mid_entry->callback_data,
+                                             mid_entry->mid);
                        }
                        spin_unlock(&GlobalMid_Lock);
                }
@@ -417,7 +418,6 @@ static const struct file_operations cifs_stats_proc_fops = {
 
 static struct proc_dir_entry *proc_fs_cifs;
 static const struct file_operations cifsFYI_proc_fops;
-static const struct file_operations cifs_oplock_proc_fops;
 static const struct file_operations cifs_lookup_cache_proc_fops;
 static const struct file_operations traceSMB_proc_fops;
 static const struct file_operations cifs_multiuser_mount_proc_fops;
@@ -438,7 +438,6 @@ cifs_proc_init(void)
 #endif /* STATS */
        proc_create("cifsFYI", 0, proc_fs_cifs, &cifsFYI_proc_fops);
        proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops);
-       proc_create("OplockEnabled", 0, proc_fs_cifs, &cifs_oplock_proc_fops);
        proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs,
                    &cifs_linux_ext_proc_fops);
        proc_create("MultiuserMount", 0, proc_fs_cifs,
@@ -462,7 +461,6 @@ cifs_proc_clean(void)
        remove_proc_entry("Stats", proc_fs_cifs);
 #endif
        remove_proc_entry("MultiuserMount", proc_fs_cifs);
-       remove_proc_entry("OplockEnabled", proc_fs_cifs);
        remove_proc_entry("SecurityFlags", proc_fs_cifs);
        remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
        remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
@@ -508,46 +506,6 @@ static const struct file_operations cifsFYI_proc_fops = {
        .write          = cifsFYI_proc_write,
 };
 
-static int cifs_oplock_proc_show(struct seq_file *m, void *v)
-{
-       seq_printf(m, "%d\n", enable_oplocks);
-       return 0;
-}
-
-static int cifs_oplock_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, cifs_oplock_proc_show, NULL);
-}
-
-static ssize_t cifs_oplock_proc_write(struct file *file,
-               const char __user *buffer, size_t count, loff_t *ppos)
-{
-       char c;
-       int rc;
-
-       printk(KERN_WARNING "CIFS: The /proc/fs/cifs/OplockEnabled interface "
-              "will be removed in kernel version 3.4. Please migrate to "
-              "using the 'enable_oplocks' module parameter in cifs.ko.\n");
-       rc = get_user(c, buffer);
-       if (rc)
-               return rc;
-       if (c == '0' || c == 'n' || c == 'N')
-               enable_oplocks = false;
-       else if (c == '1' || c == 'y' || c == 'Y')
-               enable_oplocks = true;
-
-       return count;
-}
-
-static const struct file_operations cifs_oplock_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = cifs_oplock_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-       .write          = cifs_oplock_proc_write,
-};
-
 static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
 {
        seq_printf(m, "%d\n", linuxExtEnabled);
index 8942b28cf807d5aba71fc0066c1f129606e11910..566e0ae8dc2cb64d8e10822da691caed0dc28a9d 100644 (file)
 void cifs_dump_mem(char *label, void *data, int length);
 #ifdef CONFIG_CIFS_DEBUG2
 #define DBG2 2
-void cifs_dump_detail(struct smb_hdr *);
+void cifs_dump_detail(void *);
 void cifs_dump_mids(struct TCP_Server_Info *);
 #else
 #define DBG2 0
 #endif
 extern int traceSMB;           /* flag which enables the function below */
-void dump_smb(struct smb_hdr *, int);
+void dump_smb(void *, int);
 #define CIFS_INFO      0x01
 #define CIFS_RC                0x02
 #define CIFS_TIMER     0x04
index eee522c56ef0aa5478e7e4e5919e19082f402485..d34212822444221d8698b716bc817857ab577f5d 100644 (file)
@@ -85,6 +85,8 @@ extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
 extern mempool_t *cifs_mid_poolp;
 
+struct workqueue_struct        *cifsiod_wq;
+
 static int
 cifs_read_super(struct super_block *sb)
 {
@@ -1111,9 +1113,15 @@ init_cifs(void)
                cFYI(1, "cifs_max_pending set to max of %u", CIFS_MAX_REQ);
        }
 
+       cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
+       if (!cifsiod_wq) {
+               rc = -ENOMEM;
+               goto out_clean_proc;
+       }
+
        rc = cifs_fscache_register();
        if (rc)
-               goto out_clean_proc;
+               goto out_destroy_wq;
 
        rc = cifs_init_inodecache();
        if (rc)
@@ -1161,6 +1169,8 @@ out_destroy_inodecache:
        cifs_destroy_inodecache();
 out_unreg_fscache:
        cifs_fscache_unregister();
+out_destroy_wq:
+       destroy_workqueue(cifsiod_wq);
 out_clean_proc:
        cifs_proc_clean();
        return rc;
@@ -1183,6 +1193,7 @@ exit_cifs(void)
        cifs_destroy_mids();
        cifs_destroy_inodecache();
        cifs_fscache_unregister();
+       destroy_workqueue(cifsiod_wq);
        cifs_proc_clean();
 }
 
index fe5ecf1b422a9b48a7ffb8102c7879a16fe8d6db..d1389bb33ceb98ffdc068b1192dad4f1d2b3f425 100644 (file)
@@ -125,5 +125,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
 extern const struct export_operations cifs_export_ops;
 #endif /* CONFIG_CIFS_NFSD_EXPORT */
 
-#define CIFS_VERSION   "1.76"
+#define CIFS_VERSION   "1.77"
 #endif                         /* _CIFSFS_H */
index 339ebe3ebc0da2284c3d52d83eddce4b43a0855b..4ff6313f0a9158958fbf0f63e4b21464b281c996 100644 (file)
@@ -230,6 +230,12 @@ struct cifs_mnt_data {
        int flags;
 };
 
+static inline unsigned int
+get_rfc1002_length(void *buf)
+{
+       return be32_to_cpu(*((__be32 *)buf));
+}
+
 struct TCP_Server_Info {
        struct list_head tcp_ses_list;
        struct list_head smb_ses_list;
@@ -276,7 +282,7 @@ struct TCP_Server_Info {
                                   vcnumbers */
        int capabilities; /* allow selective disabling of caps by smb sess */
        int timeAdj;  /* Adjust for difference in server time zone in sec */
-       __u16 CurrentMid;         /* multiplex id - rotating counter */
+       __u64 CurrentMid;         /* multiplex id - rotating counter */
        char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */
        /* 16th byte of RFC1001 workstation name is always null */
        char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
@@ -335,6 +341,18 @@ has_credits(struct TCP_Server_Info *server, int *credits)
        return num > 0;
 }
 
+static inline size_t
+header_size(void)
+{
+       return sizeof(struct smb_hdr);
+}
+
+static inline size_t
+max_header_size(void)
+{
+       return MAX_CIFS_HDR_SIZE;
+}
+
 /*
  * Macros to allow the TCP_Server_Info->net field and related code to drop out
  * when CONFIG_NET_NS isn't set.
@@ -583,9 +601,11 @@ struct cifs_io_parms {
  * Take a reference on the file private data. Must be called with
  * cifs_file_list_lock held.
  */
-static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file)
+static inline
+struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file)
 {
        ++cifs_file->count;
+       return cifs_file;
 }
 
 void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
@@ -606,7 +626,7 @@ struct cifsInodeInfo {
        bool delete_pending;            /* DELETE_ON_CLOSE is set */
        bool invalid_mapping;           /* pagecache is invalid */
        unsigned long time;             /* jiffies of last update of inode */
-       u64  server_eof;                /* current file size on server */
+       u64  server_eof;                /* current file size on server -- protected by i_lock */
        u64  uniqueid;                  /* server inode number */
        u64  createtime;                /* creation time on server */
 #ifdef CONFIG_CIFS_FSCACHE
@@ -713,8 +733,8 @@ typedef void (mid_callback_t)(struct mid_q_entry *mid);
 /* one of these for every pending CIFS request to the server */
 struct mid_q_entry {
        struct list_head qhead; /* mids waiting on reply from this server */
-       __u16 mid;              /* multiplex id */
-       __u16 pid;              /* process id */
+       __u64 mid;              /* multiplex id */
+       __u32 pid;              /* process id */
        __u32 sequence_number;  /* for CIFS signing */
        unsigned long when_alloc;  /* when mid was created */
 #ifdef CONFIG_CIFS_STATS2
@@ -724,10 +744,10 @@ struct mid_q_entry {
        mid_receive_t *receive; /* call receive callback */
        mid_callback_t *callback; /* call completion callback */
        void *callback_data;      /* general purpose pointer for callback */
-       struct smb_hdr *resp_buf;       /* pointer to received SMB header */
-       int midState;   /* wish this were enum but can not pass to wait_event */
-       __u8 command;   /* smb command code */
-       bool largeBuf:1;        /* if valid response, is pointer to large buf */
+       void *resp_buf;         /* pointer to received SMB header */
+       int mid_state;  /* wish this were enum but can not pass to wait_event */
+       __le16 command;         /* smb command code */
+       bool large_buf:1;       /* if valid response, is pointer to large buf */
        bool multiRsp:1;        /* multiple trans2 responses for one request  */
        bool multiEnd:1;        /* both received */
 };
@@ -1052,5 +1072,6 @@ GLOBAL_EXTERN spinlock_t gidsidlock;
 void cifs_oplock_break(struct work_struct *work);
 
 extern const struct slow_work_ops cifs_oplock_break_ops;
+extern struct workqueue_struct *cifsiod_wq;
 
 #endif /* _CIFS_GLOB_H */
index 503e73d8bdb7beddab4e2f495a2467e7efd17de1..96192c1e380afb9475048f5d932e886fb4338d61 100644 (file)
@@ -77,7 +77,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
                        struct smb_hdr * /* out */ ,
                        int * /* bytes returned */ , const int long_op);
 extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
-                       struct smb_hdr *in_buf, int flags);
+                           char *in_buf, int flags);
 extern int cifs_check_receive(struct mid_q_entry *mid,
                        struct TCP_Server_Info *server, bool log_error);
 extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
@@ -91,9 +91,8 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
 extern void cifs_add_credits(struct TCP_Server_Info *server,
                             const unsigned int add);
 extern void cifs_set_credits(struct TCP_Server_Info *server, const int val);
-extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
-extern bool is_valid_oplock_break(struct smb_hdr *smb,
-                                 struct TCP_Server_Info *);
+extern int checkSMB(char *buf, unsigned int length);
+extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
 extern bool backup_cred(struct cifs_sb_info *);
 extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
 extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
@@ -107,7 +106,7 @@ extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
 extern int cifs_set_port(struct sockaddr *addr, const unsigned short int port);
 extern int cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len,
                                const unsigned short int port);
-extern int map_smb_to_linux_error(struct smb_hdr *smb, bool logErr);
+extern int map_smb_to_linux_error(char *buf, bool logErr);
 extern void header_assemble(struct smb_hdr *, char /* command */ ,
                            const struct cifs_tcon *, int /* length of
                            fixed section (word count) in two byte units */);
@@ -116,7 +115,7 @@ extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
                                void **request_buf);
 extern int CIFS_SessSetup(unsigned int xid, struct cifs_ses *ses,
                             const struct nls_table *nls_cp);
-extern __u16 GetNextMid(struct TCP_Server_Info *server);
+extern __u64 GetNextMid(struct TCP_Server_Info *server);
 extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
 extern u64 cifs_UnixTimeToNT(struct timespec);
 extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
@@ -484,18 +483,25 @@ int cifs_async_readv(struct cifs_readdata *rdata);
 /* asynchronous write support */
 struct cifs_writedata {
        struct kref                     refcount;
+       struct list_head                list;
+       struct completion               done;
        enum writeback_sync_modes       sync_mode;
        struct work_struct              work;
        struct cifsFileInfo             *cfile;
        __u64                           offset;
+       pid_t                           pid;
        unsigned int                    bytes;
        int                             result;
+       void (*marshal_iov) (struct kvec *iov,
+                            struct cifs_writedata *wdata);
        unsigned int                    nr_pages;
        struct page                     *pages[1];
 };
 
 int cifs_async_writev(struct cifs_writedata *wdata);
-struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages);
+void cifs_writev_complete(struct work_struct *work);
+struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
+                                               work_func_t complete);
 void cifs_writedata_release(struct kref *refcount);
 
 #endif                 /* _CIFSPROTO_H */
index 70aac35c398f2ed4ddf6bd296e8223e68569d12d..8fecc99be34443d7368f88b4db63984b8d4f3f53 100644 (file)
@@ -696,7 +696,7 @@ CIFSSMBTDis(const int xid, struct cifs_tcon *tcon)
        if (rc)
                return rc;
 
-       rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0);
        if (rc)
                cFYI(1, "Tree disconnect failed %d", rc);
 
@@ -792,7 +792,7 @@ CIFSSMBLogoff(const int xid, struct cifs_ses *ses)
        pSMB->hdr.Uid = ses->Suid;
 
        pSMB->AndXCommand = 0xFF;
-       rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0);
 session_already_dead:
        mutex_unlock(&ses->session_mutex);
 
@@ -1414,8 +1414,7 @@ cifs_readdata_free(struct cifs_readdata *rdata)
 static int
 cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 {
-       READ_RSP *rsp = (READ_RSP *)server->smallbuf;
-       unsigned int rfclen = be32_to_cpu(rsp->hdr.smb_buf_length);
+       unsigned int rfclen = get_rfc1002_length(server->smallbuf);
        int remaining = rfclen + 4 - server->total_read;
        struct cifs_readdata *rdata = mid->callback_data;
 
@@ -1424,7 +1423,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 
                length = cifs_read_from_socket(server, server->bigbuf,
                                min_t(unsigned int, remaining,
-                                       CIFSMaxBufSize + MAX_CIFS_HDR_SIZE));
+                                       CIFSMaxBufSize + max_header_size()));
                if (length < 0)
                        return length;
                server->total_read += length;
@@ -1435,19 +1434,40 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        return 0;
 }
 
+static inline size_t
+read_rsp_size(void)
+{
+       return sizeof(READ_RSP);
+}
+
+static inline unsigned int
+read_data_offset(char *buf)
+{
+       READ_RSP *rsp = (READ_RSP *)buf;
+       return le16_to_cpu(rsp->DataOffset);
+}
+
+static inline unsigned int
+read_data_length(char *buf)
+{
+       READ_RSP *rsp = (READ_RSP *)buf;
+       return (le16_to_cpu(rsp->DataLengthHigh) << 16) +
+              le16_to_cpu(rsp->DataLength);
+}
+
 static int
 cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 {
        int length, len;
        unsigned int data_offset, remaining, data_len;
        struct cifs_readdata *rdata = mid->callback_data;
-       READ_RSP *rsp = (READ_RSP *)server->smallbuf;
-       unsigned int rfclen = be32_to_cpu(rsp->hdr.smb_buf_length) + 4;
+       char *buf = server->smallbuf;
+       unsigned int buflen = get_rfc1002_length(buf) + 4;
        u64 eof;
        pgoff_t eof_index;
        struct page *page, *tpage;
 
-       cFYI(1, "%s: mid=%u offset=%llu bytes=%u", __func__,
+       cFYI(1, "%s: mid=%llu offset=%llu bytes=%u", __func__,
                mid->mid, rdata->offset, rdata->bytes);
 
        /*
@@ -1455,10 +1475,9 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
         * can if there's not enough data. At this point, we've read down to
         * the Mid.
         */
-       len = min_t(unsigned int, rfclen, sizeof(*rsp)) -
-                       sizeof(struct smb_hdr) + 1;
+       len = min_t(unsigned int, buflen, read_rsp_size()) - header_size() + 1;
 
-       rdata->iov[0].iov_base = server->smallbuf + sizeof(struct smb_hdr) - 1;
+       rdata->iov[0].iov_base = buf + header_size() - 1;
        rdata->iov[0].iov_len = len;
 
        length = cifs_readv_from_socket(server, rdata->iov, 1, len);
@@ -1467,7 +1486,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        server->total_read += length;
 
        /* Was the SMB read successful? */
-       rdata->result = map_smb_to_linux_error(&rsp->hdr, false);
+       rdata->result = map_smb_to_linux_error(buf, false);
        if (rdata->result != 0) {
                cFYI(1, "%s: server returned error %d", __func__,
                        rdata->result);
@@ -1475,14 +1494,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        }
 
        /* Is there enough to get to the rest of the READ_RSP header? */
-       if (server->total_read < sizeof(READ_RSP)) {
+       if (server->total_read < read_rsp_size()) {
                cFYI(1, "%s: server returned short header. got=%u expected=%zu",
-                       __func__, server->total_read, sizeof(READ_RSP));
+                       __func__, server->total_read, read_rsp_size());
                rdata->result = -EIO;
                return cifs_readv_discard(server, mid);
        }
 
-       data_offset = le16_to_cpu(rsp->DataOffset) + 4;
+       data_offset = read_data_offset(buf) + 4;
        if (data_offset < server->total_read) {
                /*
                 * win2k8 sometimes sends an offset of 0 when the read
@@ -1506,7 +1525,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        len = data_offset - server->total_read;
        if (len > 0) {
                /* read any junk before data into the rest of smallbuf */
-               rdata->iov[0].iov_base = server->smallbuf + server->total_read;
+               rdata->iov[0].iov_base = buf + server->total_read;
                rdata->iov[0].iov_len = len;
                length = cifs_readv_from_socket(server, rdata->iov, 1, len);
                if (length < 0)
@@ -1515,15 +1534,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        }
 
        /* set up first iov for signature check */
-       rdata->iov[0].iov_base = server->smallbuf;
+       rdata->iov[0].iov_base = buf;
        rdata->iov[0].iov_len = server->total_read;
        cFYI(1, "0: iov_base=%p iov_len=%zu",
                rdata->iov[0].iov_base, rdata->iov[0].iov_len);
 
        /* how much data is in the response? */
-       data_len = le16_to_cpu(rsp->DataLengthHigh) << 16;
-       data_len += le16_to_cpu(rsp->DataLength);
-       if (data_offset + data_len > rfclen) {
+       data_len = read_data_length(buf);
+       if (data_offset + data_len > buflen) {
                /* data_len is corrupt -- discard frame */
                rdata->result = -EIO;
                return cifs_readv_discard(server, mid);
@@ -1602,11 +1620,11 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 
        rdata->bytes = length;
 
-       cFYI(1, "total_read=%u rfclen=%u remaining=%u", server->total_read,
-               rfclen, remaining);
+       cFYI(1, "total_read=%u buflen=%u remaining=%u", server->total_read,
+               buflen, remaining);
 
        /* discard anything left over */
-       if (server->total_read < rfclen)
+       if (server->total_read < buflen)
                return cifs_readv_discard(server, mid);
 
        dequeue_mid(mid, false);
@@ -1647,10 +1665,10 @@ cifs_readv_callback(struct mid_q_entry *mid)
        struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
        struct TCP_Server_Info *server = tcon->ses->server;
 
-       cFYI(1, "%s: mid=%u state=%d result=%d bytes=%u", __func__,
-               mid->mid, mid->midState, rdata->result, rdata->bytes);
+       cFYI(1, "%s: mid=%llu state=%d result=%d bytes=%u", __func__,
+               mid->mid, mid->mid_state, rdata->result, rdata->bytes);
 
-       switch (mid->midState) {
+       switch (mid->mid_state) {
        case MID_RESPONSE_RECEIVED:
                /* result already set, check signature */
                if (server->sec_mode &
@@ -1671,7 +1689,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
                rdata->result = -EIO;
        }
 
-       queue_work(system_nrt_wq, &rdata->work);
+       queue_work(cifsiod_wq, &rdata->work);
        DeleteMidQEntry(mid);
        cifs_add_credits(server, 1);
 }
@@ -2017,7 +2035,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
        kref_put(&wdata->refcount, cifs_writedata_release);
 }
 
-static void
+void
 cifs_writev_complete(struct work_struct *work)
 {
        struct cifs_writedata *wdata = container_of(work,
@@ -2026,7 +2044,9 @@ cifs_writev_complete(struct work_struct *work)
        int i = 0;
 
        if (wdata->result == 0) {
+               spin_lock(&inode->i_lock);
                cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes);
+               spin_unlock(&inode->i_lock);
                cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink),
                                         wdata->bytes);
        } else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN)
@@ -2047,7 +2067,7 @@ cifs_writev_complete(struct work_struct *work)
 }
 
 struct cifs_writedata *
-cifs_writedata_alloc(unsigned int nr_pages)
+cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)
 {
        struct cifs_writedata *wdata;
 
@@ -2061,14 +2081,16 @@ cifs_writedata_alloc(unsigned int nr_pages)
        wdata = kzalloc(sizeof(*wdata) +
                        sizeof(struct page *) * (nr_pages - 1), GFP_NOFS);
        if (wdata != NULL) {
-               INIT_WORK(&wdata->work, cifs_writev_complete);
                kref_init(&wdata->refcount);
+               INIT_LIST_HEAD(&wdata->list);
+               init_completion(&wdata->done);
+               INIT_WORK(&wdata->work, complete);
        }
        return wdata;
 }
 
 /*
- * Check the midState and signature on received buffer (if any), and queue the
+ * Check the mid_state and signature on received buffer (if any), and queue the
  * workqueue completion task.
  */
 static void
@@ -2079,7 +2101,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
        unsigned int written;
        WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf;
 
-       switch (mid->midState) {
+       switch (mid->mid_state) {
        case MID_RESPONSE_RECEIVED:
                wdata->result = cifs_check_receive(mid, tcon->ses->server, 0);
                if (wdata->result != 0)
@@ -2111,7 +2133,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
                break;
        }
 
-       queue_work(system_nrt_wq, &wdata->work);
+       queue_work(cifsiod_wq, &wdata->work);
        DeleteMidQEntry(mid);
        cifs_add_credits(tcon->ses->server, 1);
 }
@@ -2124,7 +2146,6 @@ cifs_async_writev(struct cifs_writedata *wdata)
        WRITE_REQ *smb = NULL;
        int wct;
        struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
-       struct inode *inode = wdata->cfile->dentry->d_inode;
        struct kvec *iov = NULL;
 
        if (tcon->ses->capabilities & CAP_LARGE_FILES) {
@@ -2148,8 +2169,8 @@ cifs_async_writev(struct cifs_writedata *wdata)
                goto async_writev_out;
        }
 
-       smb->hdr.Pid = cpu_to_le16((__u16)wdata->cfile->pid);
-       smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->cfile->pid >> 16));
+       smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid);
+       smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16));
 
        smb->AndXCommand = 0xFF;        /* none */
        smb->Fid = wdata->cfile->netfid;
@@ -2167,15 +2188,13 @@ cifs_async_writev(struct cifs_writedata *wdata)
        iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1;
        iov[0].iov_base = smb;
 
-       /* marshal up the pages into iov array */
-       wdata->bytes = 0;
-       for (i = 0; i < wdata->nr_pages; i++) {
-               iov[i + 1].iov_len = min(inode->i_size -
-                                     page_offset(wdata->pages[i]),
-                                       (loff_t)PAGE_CACHE_SIZE);
-               iov[i + 1].iov_base = kmap(wdata->pages[i]);
-               wdata->bytes += iov[i + 1].iov_len;
-       }
+       /*
+        * This function should marshal up the page array into the kvec
+        * array, reserving [0] for the header. It should kmap the pages
+        * and set the iov_len properly for each one. It may also set
+        * wdata->bytes too.
+        */
+       wdata->marshal_iov(iov, wdata);
 
        cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
 
@@ -2420,8 +2439,7 @@ CIFSSMBLock(const int xid, struct cifs_tcon *tcon,
                        (struct smb_hdr *) pSMB, &bytes_returned);
                cifs_small_buf_release(pSMB);
        } else {
-               rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
-                                     timeout);
+               rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, timeout);
                /* SMB buffer freed by function above */
        }
        cifs_stats_inc(&tcon->num_locks);
@@ -2588,7 +2606,7 @@ CIFSSMBClose(const int xid, struct cifs_tcon *tcon, int smb_file_id)
        pSMB->FileID = (__u16) smb_file_id;
        pSMB->LastWriteTime = 0xFFFFFFFF;
        pSMB->ByteCount = 0;
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        cifs_stats_inc(&tcon->num_closes);
        if (rc) {
                if (rc != -EINTR) {
@@ -2617,7 +2635,7 @@ CIFSSMBFlush(const int xid, struct cifs_tcon *tcon, int smb_file_id)
 
        pSMB->FileID = (__u16) smb_file_id;
        pSMB->ByteCount = 0;
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        cifs_stats_inc(&tcon->num_flushes);
        if (rc)
                cERROR(1, "Send error in Flush = %d", rc);
@@ -4625,7 +4643,7 @@ CIFSFindClose(const int xid, struct cifs_tcon *tcon,
 
        pSMB->FileID = searchHandle;
        pSMB->ByteCount = 0;
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc)
                cERROR(1, "Send error in FindClose = %d", rc);
 
@@ -5646,7 +5664,7 @@ CIFSSMBSetFileSize(const int xid, struct cifs_tcon *tcon, __u64 size,
        pSMB->Reserved4 = 0;
        inc_rfc1001_len(pSMB, byte_count);
        pSMB->ByteCount = cpu_to_le16(byte_count);
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc) {
                cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc);
        }
@@ -5715,7 +5733,7 @@ CIFSSMBSetFileInfo(const int xid, struct cifs_tcon *tcon,
        inc_rfc1001_len(pSMB, byte_count);
        pSMB->ByteCount = cpu_to_le16(byte_count);
        memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc)
                cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
 
@@ -5774,7 +5792,7 @@ CIFSSMBSetFileDisposition(const int xid, struct cifs_tcon *tcon,
        inc_rfc1001_len(pSMB, byte_count);
        pSMB->ByteCount = cpu_to_le16(byte_count);
        *data_offset = delete_file ? 1 : 0;
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc)
                cFYI(1, "Send error in SetFileDisposition = %d", rc);
 
@@ -6006,7 +6024,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon,
 
        cifs_fill_unix_set_info(data_offset, args);
 
-       rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
+       rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
        if (rc)
                cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
 
index 5560e1d5e54b5f7fe7f2f7d6bf9fe0a2d2a8852f..302a15c505a954882462dc42c1a94179e60b8c78 100644 (file)
@@ -40,6 +40,8 @@
 #include <linux/module.h>
 #include <keys/user-type.h>
 #include <net/ipv6.h>
+#include <linux/parser.h>
+
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsproto.h"
@@ -63,6 +65,193 @@ extern mempool_t *cifs_req_poolp;
 #define TLINK_ERROR_EXPIRE     (1 * HZ)
 #define TLINK_IDLE_EXPIRE      (600 * HZ)
 
+enum {
+
+       /* Mount options that take no arguments */
+       Opt_user_xattr, Opt_nouser_xattr,
+       Opt_forceuid, Opt_noforceuid,
+       Opt_noblocksend, Opt_noautotune,
+       Opt_hard, Opt_soft, Opt_perm, Opt_noperm,
+       Opt_mapchars, Opt_nomapchars, Opt_sfu,
+       Opt_nosfu, Opt_nodfs, Opt_posixpaths,
+       Opt_noposixpaths, Opt_nounix,
+       Opt_nocase,
+       Opt_brl, Opt_nobrl,
+       Opt_forcemandatorylock, Opt_setuids,
+       Opt_nosetuids, Opt_dynperm, Opt_nodynperm,
+       Opt_nohard, Opt_nosoft,
+       Opt_nointr, Opt_intr,
+       Opt_nostrictsync, Opt_strictsync,
+       Opt_serverino, Opt_noserverino,
+       Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl,
+       Opt_acl, Opt_noacl, Opt_locallease,
+       Opt_sign, Opt_seal, Opt_direct,
+       Opt_strictcache, Opt_noac,
+       Opt_fsc, Opt_mfsymlinks,
+       Opt_multiuser, Opt_sloppy,
+
+       /* Mount options which take numeric value */
+       Opt_backupuid, Opt_backupgid, Opt_uid,
+       Opt_cruid, Opt_gid, Opt_file_mode,
+       Opt_dirmode, Opt_port,
+       Opt_rsize, Opt_wsize, Opt_actimeo,
+
+       /* Mount options which take string value */
+       Opt_user, Opt_pass, Opt_ip,
+       Opt_unc, Opt_domain,
+       Opt_srcaddr, Opt_prefixpath,
+       Opt_iocharset, Opt_sockopt,
+       Opt_netbiosname, Opt_servern,
+       Opt_ver, Opt_sec,
+
+       /* Mount options to be ignored */
+       Opt_ignore,
+
+       /* Options which could be blank */
+       Opt_blank_pass,
+
+       Opt_err
+};
+
+static const match_table_t cifs_mount_option_tokens = {
+
+       { Opt_user_xattr, "user_xattr" },
+       { Opt_nouser_xattr, "nouser_xattr" },
+       { Opt_forceuid, "forceuid" },
+       { Opt_noforceuid, "noforceuid" },
+       { Opt_noblocksend, "noblocksend" },
+       { Opt_noautotune, "noautotune" },
+       { Opt_hard, "hard" },
+       { Opt_soft, "soft" },
+       { Opt_perm, "perm" },
+       { Opt_noperm, "noperm" },
+       { Opt_mapchars, "mapchars" },
+       { Opt_nomapchars, "nomapchars" },
+       { Opt_sfu, "sfu" },
+       { Opt_nosfu, "nosfu" },
+       { Opt_nodfs, "nodfs" },
+       { Opt_posixpaths, "posixpaths" },
+       { Opt_noposixpaths, "noposixpaths" },
+       { Opt_nounix, "nounix" },
+       { Opt_nounix, "nolinux" },
+       { Opt_nocase, "nocase" },
+       { Opt_nocase, "ignorecase" },
+       { Opt_brl, "brl" },
+       { Opt_nobrl, "nobrl" },
+       { Opt_nobrl, "nolock" },
+       { Opt_forcemandatorylock, "forcemandatorylock" },
+       { Opt_forcemandatorylock, "forcemand" },
+       { Opt_setuids, "setuids" },
+       { Opt_nosetuids, "nosetuids" },
+       { Opt_dynperm, "dynperm" },
+       { Opt_nodynperm, "nodynperm" },
+       { Opt_nohard, "nohard" },
+       { Opt_nosoft, "nosoft" },
+       { Opt_nointr, "nointr" },
+       { Opt_intr, "intr" },
+       { Opt_nostrictsync, "nostrictsync" },
+       { Opt_strictsync, "strictsync" },
+       { Opt_serverino, "serverino" },
+       { Opt_noserverino, "noserverino" },
+       { Opt_rwpidforward, "rwpidforward" },
+       { Opt_cifsacl, "cifsacl" },
+       { Opt_nocifsacl, "nocifsacl" },
+       { Opt_acl, "acl" },
+       { Opt_noacl, "noacl" },
+       { Opt_locallease, "locallease" },
+       { Opt_sign, "sign" },
+       { Opt_seal, "seal" },
+       { Opt_direct, "direct" },
+       { Opt_direct, "forceddirectio" },
+       { Opt_strictcache, "strictcache" },
+       { Opt_noac, "noac" },
+       { Opt_fsc, "fsc" },
+       { Opt_mfsymlinks, "mfsymlinks" },
+       { Opt_multiuser, "multiuser" },
+       { Opt_sloppy, "sloppy" },
+
+       { Opt_backupuid, "backupuid=%s" },
+       { Opt_backupgid, "backupgid=%s" },
+       { Opt_uid, "uid=%s" },
+       { Opt_cruid, "cruid=%s" },
+       { Opt_gid, "gid=%s" },
+       { Opt_file_mode, "file_mode=%s" },
+       { Opt_dirmode, "dirmode=%s" },
+       { Opt_dirmode, "dir_mode=%s" },
+       { Opt_port, "port=%s" },
+       { Opt_rsize, "rsize=%s" },
+       { Opt_wsize, "wsize=%s" },
+       { Opt_actimeo, "actimeo=%s" },
+
+       { Opt_user, "user=%s" },
+       { Opt_user, "username=%s" },
+       { Opt_blank_pass, "pass=" },
+       { Opt_pass, "pass=%s" },
+       { Opt_pass, "password=%s" },
+       { Opt_ip, "ip=%s" },
+       { Opt_ip, "addr=%s" },
+       { Opt_unc, "unc=%s" },
+       { Opt_unc, "target=%s" },
+       { Opt_unc, "path=%s" },
+       { Opt_domain, "dom=%s" },
+       { Opt_domain, "domain=%s" },
+       { Opt_domain, "workgroup=%s" },
+       { Opt_srcaddr, "srcaddr=%s" },
+       { Opt_prefixpath, "prefixpath=%s" },
+       { Opt_iocharset, "iocharset=%s" },
+       { Opt_sockopt, "sockopt=%s" },
+       { Opt_netbiosname, "netbiosname=%s" },
+       { Opt_servern, "servern=%s" },
+       { Opt_ver, "ver=%s" },
+       { Opt_ver, "vers=%s" },
+       { Opt_ver, "version=%s" },
+       { Opt_sec, "sec=%s" },
+
+       { Opt_ignore, "cred" },
+       { Opt_ignore, "credentials" },
+       { Opt_ignore, "guest" },
+       { Opt_ignore, "rw" },
+       { Opt_ignore, "ro" },
+       { Opt_ignore, "suid" },
+       { Opt_ignore, "nosuid" },
+       { Opt_ignore, "exec" },
+       { Opt_ignore, "noexec" },
+       { Opt_ignore, "nodev" },
+       { Opt_ignore, "noauto" },
+       { Opt_ignore, "dev" },
+       { Opt_ignore, "mand" },
+       { Opt_ignore, "nomand" },
+       { Opt_ignore, "_netdev" },
+
+       { Opt_err, NULL }
+};
+
+enum {
+       Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p,
+       Opt_sec_ntlmsspi, Opt_sec_ntlmssp,
+       Opt_ntlm, Opt_sec_ntlmi, Opt_sec_ntlmv2i,
+       Opt_sec_nontlm, Opt_sec_lanman,
+       Opt_sec_none,
+
+       Opt_sec_err
+};
+
+static const match_table_t cifs_secflavor_tokens = {
+       { Opt_sec_krb5, "krb5" },
+       { Opt_sec_krb5i, "krb5i" },
+       { Opt_sec_krb5p, "krb5p" },
+       { Opt_sec_ntlmsspi, "ntlmsspi" },
+       { Opt_sec_ntlmssp, "ntlmssp" },
+       { Opt_ntlm, "ntlm" },
+       { Opt_sec_ntlmi, "ntlmi" },
+       { Opt_sec_ntlmv2i, "ntlmv2i" },
+       { Opt_sec_nontlm, "nontlm" },
+       { Opt_sec_lanman, "lanman" },
+       { Opt_sec_none, "none" },
+
+       { Opt_sec_err, NULL }
+};
+
 static int ip_connect(struct TCP_Server_Info *server);
 static int generic_ip_connect(struct TCP_Server_Info *server);
 static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
@@ -143,8 +332,8 @@ cifs_reconnect(struct TCP_Server_Info *server)
        spin_lock(&GlobalMid_Lock);
        list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
                mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
-               if (mid_entry->midState == MID_REQUEST_SUBMITTED)
-                       mid_entry->midState = MID_RETRY_NEEDED;
+               if (mid_entry->mid_state == MID_REQUEST_SUBMITTED)
+                       mid_entry->mid_state = MID_RETRY_NEEDED;
                list_move(&mid_entry->qhead, &retry_list);
        }
        spin_unlock(&GlobalMid_Lock);
@@ -183,8 +372,9 @@ cifs_reconnect(struct TCP_Server_Info *server)
                -EINVAL = invalid transact2
 
  */
-static int check2ndT2(struct smb_hdr *pSMB)
+static int check2ndT2(char *buf)
 {
+       struct smb_hdr *pSMB = (struct smb_hdr *)buf;
        struct smb_t2_rsp *pSMBt;
        int remaining;
        __u16 total_data_size, data_in_this_rsp;
@@ -224,10 +414,10 @@ static int check2ndT2(struct smb_hdr *pSMB)
        return remaining;
 }
 
-static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
+static int coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
 {
-       struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)psecond;
-       struct smb_t2_rsp *pSMBt  = (struct smb_t2_rsp *)pTargetSMB;
+       struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf;
+       struct smb_t2_rsp *pSMBt  = (struct smb_t2_rsp *)target_hdr;
        char *data_area_of_tgt;
        char *data_area_of_src;
        int remaining;
@@ -280,23 +470,23 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
        put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount);
 
        /* fix up the BCC */
-       byte_count = get_bcc(pTargetSMB);
+       byte_count = get_bcc(target_hdr);
        byte_count += total_in_src;
        /* is the result too big for the field? */
        if (byte_count > USHRT_MAX) {
                cFYI(1, "coalesced BCC too large (%u)", byte_count);
                return -EPROTO;
        }
-       put_bcc(byte_count, pTargetSMB);
+       put_bcc(byte_count, target_hdr);
 
-       byte_count = be32_to_cpu(pTargetSMB->smb_buf_length);
+       byte_count = be32_to_cpu(target_hdr->smb_buf_length);
        byte_count += total_in_src;
        /* don't allow buffer to overflow */
        if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
                cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count);
                return -ENOBUFS;
        }
-       pTargetSMB->smb_buf_length = cpu_to_be32(byte_count);
+       target_hdr->smb_buf_length = cpu_to_be32(byte_count);
 
        /* copy second buffer into end of first buffer */
        memcpy(data_area_of_tgt, data_area_of_src, total_in_src);
@@ -334,7 +524,7 @@ cifs_echo_request(struct work_struct *work)
                        server->hostname);
 
 requeue_echo:
-       queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL);
+       queue_delayed_work(cifsiod_wq, &server->echo, SMB_ECHO_INTERVAL);
 }
 
 static bool
@@ -350,7 +540,7 @@ allocate_buffers(struct TCP_Server_Info *server)
                }
        } else if (server->large_buf) {
                /* we are reusing a dirty large buf, clear its start */
-               memset(server->bigbuf, 0, sizeof(struct smb_hdr));
+               memset(server->bigbuf, 0, header_size());
        }
 
        if (!server->smallbuf) {
@@ -364,7 +554,7 @@ allocate_buffers(struct TCP_Server_Info *server)
                /* beginning of smb buffer is cleared in our buf_get */
        } else {
                /* if existing small buf clear beginning */
-               memset(server->smallbuf, 0, sizeof(struct smb_hdr));
+               memset(server->smallbuf, 0, header_size());
        }
 
        return true;
@@ -566,15 +756,16 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
 }
 
 static struct mid_q_entry *
-find_mid(struct TCP_Server_Info *server, struct smb_hdr *buf)
+find_mid(struct TCP_Server_Info *server, char *buffer)
 {
+       struct smb_hdr *buf = (struct smb_hdr *)buffer;
        struct mid_q_entry *mid;
 
        spin_lock(&GlobalMid_Lock);
        list_for_each_entry(mid, &server->pending_mid_q, qhead) {
                if (mid->mid == buf->Mid &&
-                   mid->midState == MID_REQUEST_SUBMITTED &&
-                   mid->command == buf->Command) {
+                   mid->mid_state == MID_REQUEST_SUBMITTED &&
+                   le16_to_cpu(mid->command) == buf->Command) {
                        spin_unlock(&GlobalMid_Lock);
                        return mid;
                }
@@ -591,16 +782,16 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
 #endif
        spin_lock(&GlobalMid_Lock);
        if (!malformed)
-               mid->midState = MID_RESPONSE_RECEIVED;
+               mid->mid_state = MID_RESPONSE_RECEIVED;
        else
-               mid->midState = MID_RESPONSE_MALFORMED;
+               mid->mid_state = MID_RESPONSE_MALFORMED;
        list_del_init(&mid->qhead);
        spin_unlock(&GlobalMid_Lock);
 }
 
 static void
 handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
-          struct smb_hdr *buf, int malformed)
+          char *buf, int malformed)
 {
        if (malformed == 0 && check2ndT2(buf) > 0) {
                mid->multiRsp = true;
@@ -620,13 +811,13 @@ handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
                } else {
                        /* Have first buffer */
                        mid->resp_buf = buf;
-                       mid->largeBuf = true;
+                       mid->large_buf = true;
                        server->bigbuf = NULL;
                }
                return;
        }
        mid->resp_buf = buf;
-       mid->largeBuf = server->large_buf;
+       mid->large_buf = server->large_buf;
        /* Was previous buf put in mpx struct for multi-rsp? */
        if (!mid->multiRsp) {
                /* smb buffer will be freed by user thread */
@@ -682,8 +873,8 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
                spin_lock(&GlobalMid_Lock);
                list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
                        mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
-                       cFYI(1, "Clearing mid 0x%x", mid_entry->mid);
-                       mid_entry->midState = MID_SHUTDOWN;
+                       cFYI(1, "Clearing mid 0x%llx", mid_entry->mid);
+                       mid_entry->mid_state = MID_SHUTDOWN;
                        list_move(&mid_entry->qhead, &dispose_list);
                }
                spin_unlock(&GlobalMid_Lock);
@@ -691,7 +882,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
                /* now walk dispose list and issue callbacks */
                list_for_each_safe(tmp, tmp2, &dispose_list) {
                        mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
-                       cFYI(1, "Callback mid 0x%x", mid_entry->mid);
+                       cFYI(1, "Callback mid 0x%llx", mid_entry->mid);
                        list_del_init(&mid_entry->qhead);
                        mid_entry->callback(mid_entry);
                }
@@ -731,11 +922,10 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 {
        int length;
        char *buf = server->smallbuf;
-       struct smb_hdr *smb_buffer = (struct smb_hdr *)buf;
-       unsigned int pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
+       unsigned int pdu_length = get_rfc1002_length(buf);
 
        /* make sure this will fit in a large buffer */
-       if (pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+       if (pdu_length > CIFSMaxBufSize + max_header_size() - 4) {
                cERROR(1, "SMB response too long (%u bytes)",
                        pdu_length);
                cifs_reconnect(server);
@@ -746,20 +936,18 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        /* switch to large buffer if too big for a small one */
        if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
                server->large_buf = true;
-               memcpy(server->bigbuf, server->smallbuf, server->total_read);
+               memcpy(server->bigbuf, buf, server->total_read);
                buf = server->bigbuf;
-               smb_buffer = (struct smb_hdr *)buf;
        }
 
        /* now read the rest */
-       length = cifs_read_from_socket(server,
-                         buf + sizeof(struct smb_hdr) - 1,
-                         pdu_length - sizeof(struct smb_hdr) + 1 + 4);
+       length = cifs_read_from_socket(server, buf + header_size() - 1,
+                                      pdu_length - header_size() + 1 + 4);
        if (length < 0)
                return length;
        server->total_read += length;
 
-       dump_smb(smb_buffer, server->total_read);
+       dump_smb(buf, server->total_read);
 
        /*
         * We know that we received enough to get to the MID as we
@@ -770,7 +958,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
         * 48 bytes is enough to display the header and a little bit
         * into the payload for debugging purposes.
         */
-       length = checkSMB(smb_buffer, smb_buffer->Mid, server->total_read);
+       length = checkSMB(buf, server->total_read);
        if (length != 0)
                cifs_dump_mem("Bad SMB: ", buf,
                        min_t(unsigned int, server->total_read, 48));
@@ -778,7 +966,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        if (!mid)
                return length;
 
-       handle_mid(mid, server, smb_buffer, length);
+       handle_mid(mid, server, buf, length);
        return 0;
 }
 
@@ -789,7 +977,6 @@ cifs_demultiplex_thread(void *p)
        struct TCP_Server_Info *server = p;
        unsigned int pdu_length;
        char *buf = NULL;
-       struct smb_hdr *smb_buffer = NULL;
        struct task_struct *task_to_wake = NULL;
        struct mid_q_entry *mid_entry;
 
@@ -810,7 +997,6 @@ cifs_demultiplex_thread(void *p)
                        continue;
 
                server->large_buf = false;
-               smb_buffer = (struct smb_hdr *)server->smallbuf;
                buf = server->smallbuf;
                pdu_length = 4; /* enough to get RFC1001 header */
 
@@ -823,14 +1009,14 @@ cifs_demultiplex_thread(void *p)
                 * The right amount was read from socket - 4 bytes,
                 * so we can now interpret the length field.
                 */
-               pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
+               pdu_length = get_rfc1002_length(buf);
 
                cFYI(1, "RFC1002 header 0x%x", pdu_length);
                if (!is_smb_response(server, buf[0]))
                        continue;
 
                /* make sure we have enough to get to the MID */
-               if (pdu_length < sizeof(struct smb_hdr) - 1 - 4) {
+               if (pdu_length < header_size() - 1 - 4) {
                        cERROR(1, "SMB response too short (%u bytes)",
                                pdu_length);
                        cifs_reconnect(server);
@@ -840,12 +1026,12 @@ cifs_demultiplex_thread(void *p)
 
                /* read down to the MID */
                length = cifs_read_from_socket(server, buf + 4,
-                                       sizeof(struct smb_hdr) - 1 - 4);
+                                              header_size() - 1 - 4);
                if (length < 0)
                        continue;
                server->total_read += length;
 
-               mid_entry = find_mid(server, smb_buffer);
+               mid_entry = find_mid(server, buf);
 
                if (!mid_entry || !mid_entry->receive)
                        length = standard_receive3(server, mid_entry);
@@ -855,22 +1041,19 @@ cifs_demultiplex_thread(void *p)
                if (length < 0)
                        continue;
 
-               if (server->large_buf) {
+               if (server->large_buf)
                        buf = server->bigbuf;
-                       smb_buffer = (struct smb_hdr *)buf;
-               }
 
                server->lstrp = jiffies;
                if (mid_entry != NULL) {
                        if (!mid_entry->multiRsp || mid_entry->multiEnd)
                                mid_entry->callback(mid_entry);
-               } else if (!is_valid_oplock_break(smb_buffer, server)) {
+               } else if (!is_valid_oplock_break(buf, server)) {
                        cERROR(1, "No task to wake, unknown frame received! "
                                   "NumMids %d", atomic_read(&midCount));
-                       cifs_dump_mem("Received Data is: ", buf,
-                                     sizeof(struct smb_hdr));
+                       cifs_dump_mem("Received Data is: ", buf, header_size());
 #ifdef CONFIG_CIFS_DEBUG2
-                       cifs_dump_detail(smb_buffer);
+                       cifs_dump_detail(buf);
                        cifs_dump_mids(server);
 #endif /* CIFS_DEBUG2 */
 
@@ -926,23 +1109,95 @@ extract_hostname(const char *unc)
        return dst;
 }
 
+static int get_option_ul(substring_t args[], unsigned long *option)
+{
+       int rc;
+       char *string;
+
+       string = match_strdup(args);
+       if (string == NULL)
+               return -ENOMEM;
+       rc = kstrtoul(string, 10, option);
+       kfree(string);
+
+       return rc;
+}
+
+
+static int cifs_parse_security_flavors(char *value,
+                                      struct smb_vol *vol)
+{
+
+       substring_t args[MAX_OPT_ARGS];
+
+       switch (match_token(value, cifs_secflavor_tokens, args)) {
+       case Opt_sec_krb5:
+               vol->secFlg |= CIFSSEC_MAY_KRB5;
+               break;
+       case Opt_sec_krb5i:
+               vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN;
+               break;
+       case Opt_sec_krb5p:
+               /* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */
+               cERROR(1, "Krb5 cifs privacy not supported");
+               break;
+       case Opt_sec_ntlmssp:
+               vol->secFlg |= CIFSSEC_MAY_NTLMSSP;
+               break;
+       case Opt_sec_ntlmsspi:
+               vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN;
+               break;
+       case Opt_ntlm:
+               /* ntlm is default so can be turned off too */
+               vol->secFlg |= CIFSSEC_MAY_NTLM;
+               break;
+       case Opt_sec_ntlmi:
+               vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN;
+               break;
+       case Opt_sec_nontlm:
+               vol->secFlg |= CIFSSEC_MAY_NTLMV2;
+               break;
+       case Opt_sec_ntlmv2i:
+               vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN;
+               break;
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+       case Opt_sec_lanman:
+               vol->secFlg |= CIFSSEC_MAY_LANMAN;
+               break;
+#endif
+       case Opt_sec_none:
+               vol->nullauth = 1;
+               break;
+       default:
+               cERROR(1, "bad security option: %s", value);
+               return 1;
+       }
+
+       return 0;
+}
+
 static int
 cifs_parse_mount_options(const char *mountdata, const char *devname,
                         struct smb_vol *vol)
 {
-       char *value, *data, *end;
+       char *data, *end;
        char *mountdata_copy = NULL, *options;
-       int err;
        unsigned int  temp_len, i, j;
        char separator[2];
        short int override_uid = -1;
        short int override_gid = -1;
        bool uid_specified = false;
        bool gid_specified = false;
+       bool sloppy = false;
+       char *invalid = NULL;
        char *nodename = utsname()->nodename;
+       char *string = NULL;
+       char *tmp_end, *value;
+       char delim;
 
        separator[0] = ',';
        separator[1] = 0;
+       delim = separator[0];
 
        /*
         * does not have to be perfect mapping since field is
@@ -981,6 +1236,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 
        options = mountdata_copy;
        end = options + strlen(options);
+
        if (strncmp(options, "sep=", 4) == 0) {
                if (options[4] != 0) {
                        separator[0] = options[4];
@@ -993,609 +1249,652 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
        vol->backupgid_specified = false; /* no backup intent for a group */
 
        while ((data = strsep(&options, separator)) != NULL) {
+               substring_t args[MAX_OPT_ARGS];
+               unsigned long option;
+               int token;
+
                if (!*data)
                        continue;
-               if ((value = strchr(data, '=')) != NULL)
-                       *value++ = '\0';
 
-               /* Have to parse this before we parse for "user" */
-               if (strnicmp(data, "user_xattr", 10) == 0) {
+               token = match_token(data, cifs_mount_option_tokens, args);
+
+               switch (token) {
+
+               /* Ingnore the following */
+               case Opt_ignore:
+                       break;
+
+               /* Boolean values */
+               case Opt_user_xattr:
                        vol->no_xattr = 0;
-               } else if (strnicmp(data, "nouser_xattr", 12) == 0) {
+                       break;
+               case Opt_nouser_xattr:
                        vol->no_xattr = 1;
-               } else if (strnicmp(data, "user", 4) == 0) {
-                       if (!value) {
-                               printk(KERN_WARNING
-                                      "CIFS: invalid or missing username\n");
-                               goto cifs_parse_mount_err;
-                       } else if (!*value) {
-                               /* null user, ie anonymous, authentication */
-                               vol->nullauth = 1;
-                       }
-                       if (strnlen(value, MAX_USERNAME_SIZE) <
-                                               MAX_USERNAME_SIZE) {
-                               vol->username = kstrdup(value, GFP_KERNEL);
-                               if (!vol->username) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for username\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                       } else {
-                               printk(KERN_WARNING "CIFS: username too long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "pass", 4) == 0) {
-                       if (!value) {
-                               vol->password = NULL;
-                               continue;
-                       } else if (value[0] == 0) {
-                               /* check if string begins with double comma
-                                  since that would mean the password really
-                                  does start with a comma, and would not
-                                  indicate an empty string */
-                               if (value[1] != separator[0]) {
-                                       vol->password = NULL;
-                                       continue;
-                               }
-                       }
-                       temp_len = strlen(value);
-                       /* removed password length check, NTLM passwords
-                               can be arbitrarily long */
-
-                       /* if comma in password, the string will be
-                       prematurely null terminated.  Commas in password are
-                       specified across the cifs mount interface by a double
-                       comma ie ,, and a comma used as in other cases ie ','
-                       as a parameter delimiter/separator is single and due
-                       to the strsep above is temporarily zeroed. */
-
-                       /* NB: password legally can have multiple commas and
-                       the only illegal character in a password is null */
-
-                       if ((value[temp_len] == 0) &&
-                           (value + temp_len < end) &&
-                           (value[temp_len+1] == separator[0])) {
-                               /* reinsert comma */
-                               value[temp_len] = separator[0];
-                               temp_len += 2;  /* move after second comma */
-                               while (value[temp_len] != 0)  {
-                                       if (value[temp_len] == separator[0]) {
-                                               if (value[temp_len+1] ==
-                                                    separator[0]) {
-                                               /* skip second comma */
-                                                       temp_len++;
-                                               } else {
-                                               /* single comma indicating start
-                                                        of next parm */
-                                                       break;
-                                               }
-                                       }
-                                       temp_len++;
-                               }
-                               if (value[temp_len] == 0) {
-                                       options = NULL;
-                               } else {
-                                       value[temp_len] = 0;
-                                       /* point option to start of next parm */
-                                       options = value + temp_len + 1;
-                               }
-                               /* go from value to value + temp_len condensing
-                               double commas to singles. Note that this ends up
-                               allocating a few bytes too many, which is ok */
-                               vol->password = kzalloc(temp_len, GFP_KERNEL);
-                               if (vol->password == NULL) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for password\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                               for (i = 0, j = 0; i < temp_len; i++, j++) {
-                                       vol->password[j] = value[i];
-                                       if (value[i] == separator[0]
-                                               && value[i+1] == separator[0]) {
-                                               /* skip second comma */
-                                               i++;
-                                       }
-                               }
-                               vol->password[j] = 0;
-                       } else {
-                               vol->password = kzalloc(temp_len+1, GFP_KERNEL);
-                               if (vol->password == NULL) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for password\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                               strcpy(vol->password, value);
-                       }
-               } else if (!strnicmp(data, "ip", 2) ||
-                          !strnicmp(data, "addr", 4)) {
-                       if (!value || !*value) {
-                               vol->UNCip = NULL;
-                       } else if (strnlen(value, INET6_ADDRSTRLEN) <
-                                                       INET6_ADDRSTRLEN) {
-                               vol->UNCip = kstrdup(value, GFP_KERNEL);
-                               if (!vol->UNCip) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for UNC IP\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                       } else {
-                               printk(KERN_WARNING "CIFS: ip address "
-                                                   "too long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "sec", 3) == 0) {
-                       if (!value || !*value) {
-                               cERROR(1, "no security value specified");
-                               continue;
-                       } else if (strnicmp(value, "krb5i", 5) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_KRB5 |
-                                       CIFSSEC_MUST_SIGN;
-                       } else if (strnicmp(value, "krb5p", 5) == 0) {
-                               /* vol->secFlg |= CIFSSEC_MUST_SEAL |
-                                       CIFSSEC_MAY_KRB5; */
-                               cERROR(1, "Krb5 cifs privacy not supported");
-                               goto cifs_parse_mount_err;
-                       } else if (strnicmp(value, "krb5", 4) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_KRB5;
-                       } else if (strnicmp(value, "ntlmsspi", 8) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLMSSP |
-                                       CIFSSEC_MUST_SIGN;
-                       } else if (strnicmp(value, "ntlmssp", 7) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLMSSP;
-                       } else if (strnicmp(value, "ntlmv2i", 7) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLMV2 |
-                                       CIFSSEC_MUST_SIGN;
-                       } else if (strnicmp(value, "ntlmv2", 6) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLMV2;
-                       } else if (strnicmp(value, "ntlmi", 5) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLM |
-                                       CIFSSEC_MUST_SIGN;
-                       } else if (strnicmp(value, "ntlm", 4) == 0) {
-                               /* ntlm is default so can be turned off too */
-                               vol->secFlg |= CIFSSEC_MAY_NTLM;
-                       } else if (strnicmp(value, "nontlm", 6) == 0) {
-                               /* BB is there a better way to do this? */
-                               vol->secFlg |= CIFSSEC_MAY_NTLMV2;
-#ifdef CONFIG_CIFS_WEAK_PW_HASH
-                       } else if (strnicmp(value, "lanman", 6) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_LANMAN;
-#endif
-                       } else if (strnicmp(value, "none", 4) == 0) {
-                               vol->nullauth = 1;
-                       } else {
-                               cERROR(1, "bad security option: %s", value);
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "vers", 3) == 0) {
-                       if (!value || !*value) {
-                               cERROR(1, "no protocol version specified"
-                                         " after vers= mount option");
-                       } else if ((strnicmp(value, "cifs", 4) == 0) ||
-                                  (strnicmp(value, "1", 1) == 0)) {
-                               /* this is the default */
-                               continue;
-                       }
-               } else if ((strnicmp(data, "unc", 3) == 0)
-                          || (strnicmp(data, "target", 6) == 0)
-                          || (strnicmp(data, "path", 4) == 0)) {
-                       if (!value || !*value) {
-                               printk(KERN_WARNING "CIFS: invalid path to "
-                                                   "network resource\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       if ((temp_len = strnlen(value, 300)) < 300) {
-                               vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
-                               if (vol->UNC == NULL)
-                                       goto cifs_parse_mount_err;
-                               strcpy(vol->UNC, value);
-                               if (strncmp(vol->UNC, "//", 2) == 0) {
-                                       vol->UNC[0] = '\\';
-                                       vol->UNC[1] = '\\';
-                               } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
-                                       printk(KERN_WARNING
-                                              "CIFS: UNC Path does not begin "
-                                              "with // or \\\\ \n");
-                                       goto cifs_parse_mount_err;
-                               }
-                       } else {
-                               printk(KERN_WARNING "CIFS: UNC name too long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if ((strnicmp(data, "domain", 3) == 0)
-                          || (strnicmp(data, "workgroup", 5) == 0)) {
-                       if (!value || !*value) {
-                               printk(KERN_WARNING "CIFS: invalid domain name\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       /* BB are there cases in which a comma can be valid in
-                       a domain name and need special handling? */
-                       if (strnlen(value, 256) < 256) {
-                               vol->domainname = kstrdup(value, GFP_KERNEL);
-                               if (!vol->domainname) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for domainname\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                               cFYI(1, "Domain name set");
-                       } else {
-                               printk(KERN_WARNING "CIFS: domain name too "
-                                                   "long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "srcaddr", 7) == 0) {
-                       vol->srcaddr.ss_family = AF_UNSPEC;
-
-                       if (!value || !*value) {
-                               printk(KERN_WARNING "CIFS: srcaddr value"
-                                      " not specified.\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       i = cifs_convert_address((struct sockaddr *)&vol->srcaddr,
-                                                value, strlen(value));
-                       if (i == 0) {
-                               printk(KERN_WARNING "CIFS:  Could not parse"
-                                      " srcaddr: %s\n",
-                                      value);
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "prefixpath", 10) == 0) {
-                       if (!value || !*value) {
-                               printk(KERN_WARNING
-                                       "CIFS: invalid path prefix\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       if ((temp_len = strnlen(value, 1024)) < 1024) {
-                               if (value[0] != '/')
-                                       temp_len++;  /* missing leading slash */
-                               vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
-                               if (vol->prepath == NULL)
-                                       goto cifs_parse_mount_err;
-                               if (value[0] != '/') {
-                                       vol->prepath[0] = '/';
-                                       strcpy(vol->prepath+1, value);
-                               } else
-                                       strcpy(vol->prepath, value);
-                               cFYI(1, "prefix path %s", vol->prepath);
-                       } else {
-                               printk(KERN_WARNING "CIFS: prefix too long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "iocharset", 9) == 0) {
-                       if (!value || !*value) {
-                               printk(KERN_WARNING "CIFS: invalid iocharset "
-                                                   "specified\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       if (strnlen(value, 65) < 65) {
-                               if (strnicmp(value, "default", 7)) {
-                                       vol->iocharset = kstrdup(value,
-                                                                GFP_KERNEL);
-
-                                       if (!vol->iocharset) {
-                                               printk(KERN_WARNING "CIFS: no "
-                                                                  "memory for"
-                                                                  "charset\n");
-                                               goto cifs_parse_mount_err;
-                                       }
-                               }
-                               /* if iocharset not set then load_nls_default
-                                  is used by caller */
-                               cFYI(1, "iocharset set to %s", value);
-                       } else {
-                               printk(KERN_WARNING "CIFS: iocharset name "
-                                                   "too long.\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (!strnicmp(data, "uid", 3) && value && *value) {
-                       vol->linux_uid = simple_strtoul(value, &value, 0);
-                       uid_specified = true;
-               } else if (!strnicmp(data, "cruid", 5) && value && *value) {
-                       vol->cred_uid = simple_strtoul(value, &value, 0);
-               } else if (!strnicmp(data, "forceuid", 8)) {
+                       break;
+               case Opt_forceuid:
                        override_uid = 1;
-               } else if (!strnicmp(data, "noforceuid", 10)) {
+                       break;
+               case Opt_noforceuid:
                        override_uid = 0;
-               } else if (!strnicmp(data, "gid", 3) && value && *value) {
-                       vol->linux_gid = simple_strtoul(value, &value, 0);
-                       gid_specified = true;
-               } else if (!strnicmp(data, "forcegid", 8)) {
-                       override_gid = 1;
-               } else if (!strnicmp(data, "noforcegid", 10)) {
-                       override_gid = 0;
-               } else if (strnicmp(data, "file_mode", 4) == 0) {
-                       if (value && *value) {
-                               vol->file_mode =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "dir_mode", 4) == 0) {
-                       if (value && *value) {
-                               vol->dir_mode =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "dirmode", 4) == 0) {
-                       if (value && *value) {
-                               vol->dir_mode =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "port", 4) == 0) {
-                       if (value && *value) {
-                               vol->port =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "rsize", 5) == 0) {
-                       if (value && *value) {
-                               vol->rsize =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "wsize", 5) == 0) {
-                       if (value && *value) {
-                               vol->wsize =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "sockopt", 5) == 0) {
-                       if (!value || !*value) {
-                               cERROR(1, "no socket option specified");
-                               continue;
-                       } else if (strnicmp(value, "TCP_NODELAY", 11) == 0) {
-                               vol->sockopt_tcp_nodelay = 1;
-                       }
-               } else if (strnicmp(data, "netbiosname", 4) == 0) {
-                       if (!value || !*value || (*value == ' ')) {
-                               cFYI(1, "invalid (empty) netbiosname");
-                       } else {
-                               memset(vol->source_rfc1001_name, 0x20,
-                                       RFC1001_NAME_LEN);
-                               /*
-                                * FIXME: are there cases in which a comma can
-                                * be valid in workstation netbios name (and
-                                * need special handling)?
-                                */
-                               for (i = 0; i < RFC1001_NAME_LEN; i++) {
-                                       /* don't ucase netbiosname for user */
-                                       if (value[i] == 0)
-                                               break;
-                                       vol->source_rfc1001_name[i] = value[i];
-                               }
-                               /* The string has 16th byte zero still from
-                               set at top of the function  */
-                               if (i == RFC1001_NAME_LEN && value[i] != 0)
-                                       printk(KERN_WARNING "CIFS: netbiosname"
-                                               " longer than 15 truncated.\n");
-                       }
-               } else if (strnicmp(data, "servern", 7) == 0) {
-                       /* servernetbiosname specified override *SMBSERVER */
-                       if (!value || !*value || (*value == ' ')) {
-                               cFYI(1, "empty server netbiosname specified");
-                       } else {
-                               /* last byte, type, is 0x20 for servr type */
-                               memset(vol->target_rfc1001_name, 0x20,
-                                       RFC1001_NAME_LEN_WITH_NULL);
-
-                               for (i = 0; i < 15; i++) {
-                               /* BB are there cases in which a comma can be
-                                  valid in this workstation netbios name
-                                  (and need special handling)? */
-
-                               /* user or mount helper must uppercase
-                                  the netbiosname */
-                                       if (value[i] == 0)
-                                               break;
-                                       else
-                                               vol->target_rfc1001_name[i] =
-                                                               value[i];
-                               }
-                               /* The string has 16th byte zero still from
-                                  set at top of the function  */
-                               if (i == RFC1001_NAME_LEN && value[i] != 0)
-                                       printk(KERN_WARNING "CIFS: server net"
-                                       "biosname longer than 15 truncated.\n");
-                       }
-               } else if (strnicmp(data, "actimeo", 7) == 0) {
-                       if (value && *value) {
-                               vol->actimeo = HZ * simple_strtoul(value,
-                                                                  &value, 0);
-                               if (vol->actimeo > CIFS_MAX_ACTIMEO) {
-                                       cERROR(1, "CIFS: attribute cache"
-                                                       "timeout too large");
-                                       goto cifs_parse_mount_err;
-                               }
-                       }
-               } else if (strnicmp(data, "credentials", 4) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "version", 3) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "guest", 5) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "rw", 2) == 0 && strlen(data) == 2) {
-                       /* ignore */
-               } else if (strnicmp(data, "ro", 2) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "noblocksend", 11) == 0) {
+                       break;
+               case Opt_noblocksend:
                        vol->noblocksnd = 1;
-               } else if (strnicmp(data, "noautotune", 10) == 0) {
+                       break;
+               case Opt_noautotune:
                        vol->noautotune = 1;
-               } else if ((strnicmp(data, "suid", 4) == 0) ||
-                                  (strnicmp(data, "nosuid", 6) == 0) ||
-                                  (strnicmp(data, "exec", 4) == 0) ||
-                                  (strnicmp(data, "noexec", 6) == 0) ||
-                                  (strnicmp(data, "nodev", 5) == 0) ||
-                                  (strnicmp(data, "noauto", 6) == 0) ||
-                                  (strnicmp(data, "dev", 3) == 0)) {
-                       /*  The mount tool or mount.cifs helper (if present)
-                           uses these opts to set flags, and the flags are read
-                           by the kernel vfs layer before we get here (ie
-                           before read super) so there is no point trying to
-                           parse these options again and set anything and it
-                           is ok to just ignore them */
-                       continue;
-               } else if (strnicmp(data, "hard", 4) == 0) {
+                       break;
+               case Opt_hard:
                        vol->retry = 1;
-               } else if (strnicmp(data, "soft", 4) == 0) {
+                       break;
+               case Opt_soft:
                        vol->retry = 0;
-               } else if (strnicmp(data, "perm", 4) == 0) {
+                       break;
+               case Opt_perm:
                        vol->noperm = 0;
-               } else if (strnicmp(data, "noperm", 6) == 0) {
+                       break;
+               case Opt_noperm:
                        vol->noperm = 1;
-               } else if (strnicmp(data, "mapchars", 8) == 0) {
+                       break;
+               case Opt_mapchars:
                        vol->remap = 1;
-               } else if (strnicmp(data, "nomapchars", 10) == 0) {
+                       break;
+               case Opt_nomapchars:
                        vol->remap = 0;
-               } else if (strnicmp(data, "sfu", 3) == 0) {
+                       break;
+               case Opt_sfu:
                        vol->sfu_emul = 1;
-               } else if (strnicmp(data, "nosfu", 5) == 0) {
+                       break;
+               case Opt_nosfu:
                        vol->sfu_emul = 0;
-               } else if (strnicmp(data, "nodfs", 5) == 0) {
+                       break;
+               case Opt_nodfs:
                        vol->nodfs = 1;
-               } else if (strnicmp(data, "posixpaths", 10) == 0) {
+                       break;
+               case Opt_posixpaths:
                        vol->posix_paths = 1;
-               } else if (strnicmp(data, "noposixpaths", 12) == 0) {
+                       break;
+               case Opt_noposixpaths:
                        vol->posix_paths = 0;
-               } else if (strnicmp(data, "nounix", 6) == 0) {
-                       vol->no_linux_ext = 1;
-               } else if (strnicmp(data, "nolinux", 7) == 0) {
+                       break;
+               case Opt_nounix:
                        vol->no_linux_ext = 1;
-               } else if ((strnicmp(data, "nocase", 6) == 0) ||
-                          (strnicmp(data, "ignorecase", 10)  == 0)) {
+                       break;
+               case Opt_nocase:
                        vol->nocase = 1;
-               } else if (strnicmp(data, "mand", 4) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "nomand", 6) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "_netdev", 7) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "brl", 3) == 0) {
+                       break;
+               case Opt_brl:
                        vol->nobrl =  0;
-               } else if ((strnicmp(data, "nobrl", 5) == 0) ||
-                          (strnicmp(data, "nolock", 6) == 0)) {
+                       break;
+               case Opt_nobrl:
                        vol->nobrl =  1;
-                       /* turn off mandatory locking in mode
-                       if remote locking is turned off since the
-                       local vfs will do advisory */
+                       /*
+                        * turn off mandatory locking in mode
+                        * if remote locking is turned off since the
+                        * local vfs will do advisory
+                        */
                        if (vol->file_mode ==
                                (S_IALLUGO & ~(S_ISUID | S_IXGRP)))
                                vol->file_mode = S_IALLUGO;
-               } else if (strnicmp(data, "forcemandatorylock", 9) == 0) {
-                       /* will take the shorter form "forcemand" as well */
-                       /* This mount option will force use of mandatory
-                         (DOS/Windows style) byte range locks, instead of
-                         using posix advisory byte range locks, even if the
-                         Unix extensions are available and posix locks would
-                         be supported otherwise. If Unix extensions are not
-                         negotiated this has no effect since mandatory locks
-                         would be used (mandatory locks is all that those
-                         those servers support) */
+                       break;
+               case Opt_forcemandatorylock:
                        vol->mand_lock = 1;
-               } else if (strnicmp(data, "setuids", 7) == 0) {
+                       break;
+               case Opt_setuids:
                        vol->setuids = 1;
-               } else if (strnicmp(data, "nosetuids", 9) == 0) {
+                       break;
+               case Opt_nosetuids:
                        vol->setuids = 0;
-               } else if (strnicmp(data, "dynperm", 7) == 0) {
+                       break;
+               case Opt_dynperm:
                        vol->dynperm = true;
-               } else if (strnicmp(data, "nodynperm", 9) == 0) {
+                       break;
+               case Opt_nodynperm:
                        vol->dynperm = false;
-               } else if (strnicmp(data, "nohard", 6) == 0) {
+                       break;
+               case Opt_nohard:
                        vol->retry = 0;
-               } else if (strnicmp(data, "nosoft", 6) == 0) {
+                       break;
+               case Opt_nosoft:
                        vol->retry = 1;
-               } else if (strnicmp(data, "nointr", 6) == 0) {
+                       break;
+               case Opt_nointr:
                        vol->intr = 0;
-               } else if (strnicmp(data, "intr", 4) == 0) {
+                       break;
+               case Opt_intr:
                        vol->intr = 1;
-               } else if (strnicmp(data, "nostrictsync", 12) == 0) {
+                       break;
+               case Opt_nostrictsync:
                        vol->nostrictsync = 1;
-               } else if (strnicmp(data, "strictsync", 10) == 0) {
+                       break;
+               case Opt_strictsync:
                        vol->nostrictsync = 0;
-               } else if (strnicmp(data, "serverino", 7) == 0) {
+                       break;
+               case Opt_serverino:
                        vol->server_ino = 1;
-               } else if (strnicmp(data, "noserverino", 9) == 0) {
+                       break;
+               case Opt_noserverino:
                        vol->server_ino = 0;
-               } else if (strnicmp(data, "rwpidforward", 12) == 0) {
+                       break;
+               case Opt_rwpidforward:
                        vol->rwpidforward = 1;
-               } else if (strnicmp(data, "cifsacl", 7) == 0) {
+                       break;
+               case Opt_cifsacl:
                        vol->cifs_acl = 1;
-               } else if (strnicmp(data, "nocifsacl", 9) == 0) {
+                       break;
+               case Opt_nocifsacl:
                        vol->cifs_acl = 0;
-               } else if (strnicmp(data, "acl", 3) == 0) {
+                       break;
+               case Opt_acl:
                        vol->no_psx_acl = 0;
-               } else if (strnicmp(data, "noacl", 5) == 0) {
+                       break;
+               case Opt_noacl:
                        vol->no_psx_acl = 1;
-               } else if (strnicmp(data, "locallease", 6) == 0) {
+                       break;
+               case Opt_locallease:
                        vol->local_lease = 1;
-               } else if (strnicmp(data, "sign", 4) == 0) {
+                       break;
+               case Opt_sign:
                        vol->secFlg |= CIFSSEC_MUST_SIGN;
-               } else if (strnicmp(data, "seal", 4) == 0) {
+                       break;
+               case Opt_seal:
                        /* we do not do the following in secFlags because seal
-                          is a per tree connection (mount) not a per socket
-                          or per-smb connection option in the protocol */
-                       /* vol->secFlg |= CIFSSEC_MUST_SEAL; */
+                        * is a per tree connection (mount) not a per socket
+                        * or per-smb connection option in the protocol
+                        * vol->secFlg |= CIFSSEC_MUST_SEAL;
+                        */
                        vol->seal = 1;
-               } else if (strnicmp(data, "direct", 6) == 0) {
-                       vol->direct_io = 1;
-               } else if (strnicmp(data, "forcedirectio", 13) == 0) {
+                       break;
+               case Opt_direct:
                        vol->direct_io = 1;
-               } else if (strnicmp(data, "strictcache", 11) == 0) {
+                       break;
+               case Opt_strictcache:
                        vol->strict_io = 1;
-               } else if (strnicmp(data, "noac", 4) == 0) {
+                       break;
+               case Opt_noac:
                        printk(KERN_WARNING "CIFS: Mount option noac not "
                                "supported. Instead set "
                                "/proc/fs/cifs/LookupCacheEnabled to 0\n");
-               } else if (strnicmp(data, "fsc", 3) == 0) {
+                       break;
+               case Opt_fsc:
 #ifndef CONFIG_CIFS_FSCACHE
                        cERROR(1, "FS-Cache support needs CONFIG_CIFS_FSCACHE "
                                  "kernel config option set");
                        goto cifs_parse_mount_err;
 #endif
                        vol->fsc = true;
-               } else if (strnicmp(data, "mfsymlinks", 10) == 0) {
+                       break;
+               case Opt_mfsymlinks:
                        vol->mfsymlinks = true;
-               } else if (strnicmp(data, "multiuser", 8) == 0) {
+                       break;
+               case Opt_multiuser:
                        vol->multiuser = true;
-               } else if (!strnicmp(data, "backupuid", 9) && value && *value) {
-                       err = kstrtouint(value, 0, &vol->backupuid);
-                       if (err < 0) {
+                       break;
+               case Opt_sloppy:
+                       sloppy = true;
+                       break;
+
+               /* Numeric Values */
+               case Opt_backupuid:
+                       if (get_option_ul(args, &option)) {
                                cERROR(1, "%s: Invalid backupuid value",
                                        __func__);
                                goto cifs_parse_mount_err;
                        }
+                       vol->backupuid = option;
                        vol->backupuid_specified = true;
-               } else if (!strnicmp(data, "backupgid", 9) && value && *value) {
-                       err = kstrtouint(value, 0, &vol->backupgid);
-                       if (err < 0) {
+                       break;
+               case Opt_backupgid:
+                       if (get_option_ul(args, &option)) {
                                cERROR(1, "%s: Invalid backupgid value",
                                        __func__);
                                goto cifs_parse_mount_err;
                        }
+                       vol->backupgid = option;
                        vol->backupgid_specified = true;
-               } else
-                       printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
-                                               data);
-       }
-       if (vol->UNC == NULL) {
-               if (devname == NULL) {
-                       printk(KERN_WARNING "CIFS: Missing UNC name for mount "
-                                               "target\n");
-                       goto cifs_parse_mount_err;
-               }
-               if ((temp_len = strnlen(devname, 300)) < 300) {
-                       vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
-                       if (vol->UNC == NULL)
+                       break;
+               case Opt_uid:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid uid value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->linux_uid = option;
+                       uid_specified = true;
+                       break;
+               case Opt_cruid:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid cruid value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->cred_uid = option;
+                       break;
+               case Opt_gid:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid gid value",
+                                               __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->linux_gid = option;
+                       gid_specified = true;
+                       break;
+               case Opt_file_mode:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid file_mode value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->file_mode = option;
+                       break;
+               case Opt_dirmode:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid dir_mode value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->dir_mode = option;
+                       break;
+               case Opt_port:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid port value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->port = option;
+                       break;
+               case Opt_rsize:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid rsize value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->rsize = option;
+                       break;
+               case Opt_wsize:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid wsize value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->wsize = option;
+                       break;
+               case Opt_actimeo:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid actimeo value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->actimeo = HZ * option;
+                       if (vol->actimeo > CIFS_MAX_ACTIMEO) {
+                               cERROR(1, "CIFS: attribute cache"
+                                         "timeout too large");
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+
+               /* String Arguments */
+
+               case Opt_user:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               /* null user, ie. anonymous authentication */
+                               vol->nullauth = 1;
+                       } else if (strnlen(string, MAX_USERNAME_SIZE) >
+                                                       MAX_USERNAME_SIZE) {
+                               printk(KERN_WARNING "CIFS: username too long\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->username = kstrdup(string, GFP_KERNEL);
+                       if (!vol->username) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for username\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+               case Opt_blank_pass:
+                       vol->password = NULL;
+                       break;
+               case Opt_pass:
+                       /* passwords have to be handled differently
+                        * to allow the character used for deliminator
+                        * to be passed within them
+                        */
+
+                       /* Obtain the value string */
+                       value = strchr(data, '=');
+                       if (value != NULL)
+                               *value++ = '\0';
+
+                       /* Set tmp_end to end of the string */
+                       tmp_end = (char *) value + strlen(value);
+
+                       /* Check if following character is the deliminator
+                        * If yes, we have encountered a double deliminator
+                        * reset the NULL character to the deliminator
+                        */
+                       if (tmp_end < end && tmp_end[1] == delim)
+                               tmp_end[0] = delim;
+
+                       /* Keep iterating until we get to a single deliminator
+                        * OR the end
+                        */
+                       while ((tmp_end = strchr(tmp_end, delim)) != NULL &&
+                              (tmp_end[1] == delim)) {
+                               tmp_end = (char *) &tmp_end[2];
+                       }
+
+                       /* Reset var options to point to next element */
+                       if (tmp_end) {
+                               tmp_end[0] = '\0';
+                               options = (char *) &tmp_end[1];
+                       } else
+                               /* Reached the end of the mount option string */
+                               options = end;
+
+                       /* Now build new password string */
+                       temp_len = strlen(value);
+                       vol->password = kzalloc(temp_len+1, GFP_KERNEL);
+                       if (vol->password == NULL) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for password\n");
                                goto cifs_parse_mount_err;
-                       strcpy(vol->UNC, devname);
-                       if (strncmp(vol->UNC, "//", 2) == 0) {
+                       }
+
+                       for (i = 0, j = 0; i < temp_len; i++, j++) {
+                               vol->password[j] = value[i];
+                               if ((value[i] == delim) &&
+                                    value[i+1] == delim)
+                                       /* skip the second deliminator */
+                                       i++;
+                       }
+                       vol->password[j] = '\0';
+                       break;
+               case Opt_ip:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               vol->UNCip = NULL;
+                       } else if (strnlen(string, INET6_ADDRSTRLEN) >
+                                               INET6_ADDRSTRLEN) {
+                               printk(KERN_WARNING "CIFS: ip address "
+                                                   "too long\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->UNCip = kstrdup(string, GFP_KERNEL);
+                       if (!vol->UNCip) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for UNC IP\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+               case Opt_unc:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               printk(KERN_WARNING "CIFS: invalid path to "
+                                                   "network resource\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       temp_len = strnlen(string, 300);
+                       if (temp_len  == 300) {
+                               printk(KERN_WARNING "CIFS: UNC name too long\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       if (strncmp(string, "//", 2) == 0) {
                                vol->UNC[0] = '\\';
                                vol->UNC[1] = '\\';
-                       } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
+                       } else if (strncmp(string, "\\\\", 2) != 0) {
                                printk(KERN_WARNING "CIFS: UNC Path does not "
-                                                   "begin with // or \\\\ \n");
+                                                   "begin with // or \\\\\n");
                                goto cifs_parse_mount_err;
                        }
-                       value = strpbrk(vol->UNC+2, "/\\");
-                       if (value)
-                               *value = '\\';
-               } else {
-                       printk(KERN_WARNING "CIFS: UNC name too long\n");
+
+                       vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
+                       if (vol->UNC == NULL) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for UNC\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       strcpy(vol->UNC, string);
+                       break;
+               case Opt_domain:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               printk(KERN_WARNING "CIFS: invalid domain"
+                                                   " name\n");
+                               goto cifs_parse_mount_err;
+                       } else if (strnlen(string, 256) == 256) {
+                               printk(KERN_WARNING "CIFS: domain name too"
+                                                   " long\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       vol->domainname = kstrdup(string, GFP_KERNEL);
+                       if (!vol->domainname) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for domainname\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       cFYI(1, "Domain name set");
+                       break;
+               case Opt_srcaddr:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               printk(KERN_WARNING "CIFS: srcaddr value not"
+                                                   " specified\n");
+                               goto cifs_parse_mount_err;
+                       } else if (!cifs_convert_address(
+                                       (struct sockaddr *)&vol->srcaddr,
+                                       string, strlen(string))) {
+                               printk(KERN_WARNING "CIFS:  Could not parse"
+                                                   " srcaddr: %s\n", string);
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+               case Opt_prefixpath:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               printk(KERN_WARNING "CIFS: Invalid path"
+                                                   " prefix\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       temp_len = strnlen(string, 1024);
+                       if (string[0] != '/')
+                               temp_len++; /* missing leading slash */
+                       if (temp_len > 1024) {
+                               printk(KERN_WARNING "CIFS: prefix too long\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
+                       if (vol->prepath == NULL) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for path prefix\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       if (string[0] != '/') {
+                               vol->prepath[0] = '/';
+                               strcpy(vol->prepath+1, string);
+                       } else
+                               strcpy(vol->prepath, string);
+
+                       break;
+               case Opt_iocharset:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               printk(KERN_WARNING "CIFS: Invalid iocharset"
+                                                   " specified\n");
+                               goto cifs_parse_mount_err;
+                       } else if (strnlen(string, 1024) >= 65) {
+                               printk(KERN_WARNING "CIFS: iocharset name "
+                                                   "too long.\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                        if (strnicmp(string, "default", 7) != 0) {
+                               vol->iocharset = kstrdup(string,
+                                                        GFP_KERNEL);
+                               if (!vol->iocharset) {
+                                       printk(KERN_WARNING "CIFS: no memory"
+                                                           "for charset\n");
+                                       goto cifs_parse_mount_err;
+                               }
+                       }
+                       /* if iocharset not set then load_nls_default
+                        * is used by caller
+                        */
+                       cFYI(1, "iocharset set to %s", string);
+                       break;
+               case Opt_sockopt:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               printk(KERN_WARNING "CIFS: No socket option"
+                                                   " specified\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       if (strnicmp(string, "TCP_NODELAY", 11) == 0)
+                               vol->sockopt_tcp_nodelay = 1;
+                       break;
+               case Opt_netbiosname:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               printk(KERN_WARNING "CIFS: Invalid (empty)"
+                                                   " netbiosname\n");
+                               break;
+                       }
+
+                       memset(vol->source_rfc1001_name, 0x20,
+                               RFC1001_NAME_LEN);
+                       /*
+                        * FIXME: are there cases in which a comma can
+                        * be valid in workstation netbios name (and
+                        * need special handling)?
+                        */
+                       for (i = 0; i < RFC1001_NAME_LEN; i++) {
+                               /* don't ucase netbiosname for user */
+                               if (string[i] == 0)
+                                       break;
+                               vol->source_rfc1001_name[i] = string[i];
+                       }
+                       /* The string has 16th byte zero still from
+                        * set at top of the function
+                        */
+                       if (i == RFC1001_NAME_LEN && string[i] != 0)
+                               printk(KERN_WARNING "CIFS: netbiosname"
+                                      " longer than 15 truncated.\n");
+
+                       break;
+               case Opt_servern:
+                       /* servernetbiosname specified override *SMBSERVER */
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               printk(KERN_WARNING "CIFS: Empty server"
+                                       " netbiosname specified\n");
+                               break;
+                       }
+                       /* last byte, type, is 0x20 for servr type */
+                       memset(vol->target_rfc1001_name, 0x20,
+                               RFC1001_NAME_LEN_WITH_NULL);
+
+                       /* BB are there cases in which a comma can be
+                          valid in this workstation netbios name
+                          (and need special handling)? */
+
+                       /* user or mount helper must uppercase the
+                          netbios name */
+                       for (i = 0; i < 15; i++) {
+                               if (string[i] == 0)
+                                       break;
+                               vol->target_rfc1001_name[i] = string[i];
+                       }
+                       /* The string has 16th byte zero still from
+                          set at top of the function  */
+                       if (i == RFC1001_NAME_LEN && string[i] != 0)
+                               printk(KERN_WARNING "CIFS: server net"
+                                      "biosname longer than 15 truncated.\n");
+                       break;
+               case Opt_ver:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               cERROR(1, "no protocol version specified"
+                                         " after vers= mount option");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       if (strnicmp(string, "cifs", 4) == 0 ||
+                           strnicmp(string, "1", 1) == 0) {
+                               /* This is the default */
+                               break;
+                       }
+                       /* For all other value, error */
+                       printk(KERN_WARNING "CIFS: Invalid version"
+                                           " specified\n");
                        goto cifs_parse_mount_err;
+               case Opt_sec:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!*string) {
+                               printk(KERN_WARNING "CIFS: no security flavor"
+                                                   " specified\n");
+                               break;
+                       }
+
+                       if (cifs_parse_security_flavors(string, vol) != 0)
+                               goto cifs_parse_mount_err;
+                       break;
+               default:
+                       /*
+                        * An option we don't recognize. Save it off for later
+                        * if we haven't already found one
+                        */
+                       if (!invalid)
+                               invalid = data;
+                       break;
                }
+               /* Free up any allocated string */
+               kfree(string);
+               string = NULL;
+       }
+
+       if (!sloppy && invalid) {
+               printk(KERN_ERR "CIFS: Unknown mount option \"%s\"\n", invalid);
+               goto cifs_parse_mount_err;
        }
 
 #ifndef CONFIG_KEYS
@@ -1625,7 +1924,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
        kfree(mountdata_copy);
        return 0;
 
+out_nomem:
+       printk(KERN_WARNING "Could not allocate temporary buffer\n");
 cifs_parse_mount_err:
+       kfree(string);
        kfree(mountdata_copy);
        return 1;
 }
@@ -1977,7 +2279,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
        cifs_fscache_get_client_cookie(tcp_ses);
 
        /* queue echo request delayed work */
-       queue_delayed_work(system_nrt_wq, &tcp_ses->echo, SMB_ECHO_INTERVAL);
+       queue_delayed_work(cifsiod_wq, &tcp_ses->echo, SMB_ECHO_INTERVAL);
 
        return tcp_ses;
 
@@ -3543,7 +3845,7 @@ remote_path_check:
        tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
        spin_unlock(&cifs_sb->tlink_tree_lock);
 
-       queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks,
+       queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
                                TLINK_IDLE_EXPIRE);
 
 mount_fail_check:
@@ -4097,6 +4399,6 @@ cifs_prune_tlinks(struct work_struct *work)
        }
        spin_unlock(&cifs_sb->tlink_tree_lock);
 
-       queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks,
+       queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
                                TLINK_IDLE_EXPIRE);
 }
index 159fcc56dc2d4c4f7ae4ac80e110cc59140f6001..460d87b7cda08b1280ac05b2f4ac3378230a4f60 100644 (file)
@@ -1399,7 +1399,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
        return rc;
 }
 
-/* update the file size (if needed) after a write */
+/*
+ * update the file size (if needed) after a write. Should be called with
+ * the inode->i_lock held
+ */
 void
 cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
                      unsigned int bytes_written)
@@ -1471,7 +1474,9 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, __u32 pid,
                                return rc;
                        }
                } else {
+                       spin_lock(&dentry->d_inode->i_lock);
                        cifs_update_eof(cifsi, *poffset, bytes_written);
+                       spin_unlock(&dentry->d_inode->i_lock);
                        *poffset += bytes_written;
                }
        }
@@ -1648,6 +1653,27 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
        return rc;
 }
 
+/*
+ * Marshal up the iov array, reserving the first one for the header. Also,
+ * set wdata->bytes.
+ */
+static void
+cifs_writepages_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata)
+{
+       int i;
+       struct inode *inode = wdata->cfile->dentry->d_inode;
+       loff_t size = i_size_read(inode);
+
+       /* marshal up the pages into iov array */
+       wdata->bytes = 0;
+       for (i = 0; i < wdata->nr_pages; i++) {
+               iov[i + 1].iov_len = min(size - page_offset(wdata->pages[i]),
+                                       (loff_t)PAGE_CACHE_SIZE);
+               iov[i + 1].iov_base = kmap(wdata->pages[i]);
+               wdata->bytes += iov[i + 1].iov_len;
+       }
+}
+
 static int cifs_writepages(struct address_space *mapping,
                           struct writeback_control *wbc)
 {
@@ -1684,7 +1710,8 @@ retry:
                tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1,
                                end - index) + 1;
 
-               wdata = cifs_writedata_alloc((unsigned int)tofind);
+               wdata = cifs_writedata_alloc((unsigned int)tofind,
+                                            cifs_writev_complete);
                if (!wdata) {
                        rc = -ENOMEM;
                        break;
@@ -1791,6 +1818,7 @@ retry:
                wdata->sync_mode = wbc->sync_mode;
                wdata->nr_pages = nr_pages;
                wdata->offset = page_offset(wdata->pages[0]);
+               wdata->marshal_iov = cifs_writepages_marshal_iov;
 
                do {
                        if (wdata->cfile != NULL)
@@ -1802,6 +1830,7 @@ retry:
                                rc = -EBADF;
                                break;
                        }
+                       wdata->pid = wdata->cfile->pid;
                        rc = cifs_async_writev(wdata);
                } while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN);
 
@@ -2043,7 +2072,7 @@ cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
        unsigned long i;
 
        for (i = 0; i < num_pages; i++) {
-               pages[i] = alloc_page(__GFP_HIGHMEM);
+               pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
                if (!pages[i]) {
                        /*
                         * save number of pages we have already allocated and
@@ -2051,15 +2080,14 @@ cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
                         */
                        num_pages = i;
                        rc = -ENOMEM;
-                       goto error;
+                       break;
                }
        }
 
-       return rc;
-
-error:
-       for (i = 0; i < num_pages; i++)
-               put_page(pages[i]);
+       if (rc) {
+               for (i = 0; i < num_pages; i++)
+                       put_page(pages[i]);
+       }
        return rc;
 }
 
@@ -2070,9 +2098,7 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
        size_t clen;
 
        clen = min_t(const size_t, len, wsize);
-       num_pages = clen / PAGE_CACHE_SIZE;
-       if (clen % PAGE_CACHE_SIZE)
-               num_pages++;
+       num_pages = DIV_ROUND_UP(clen, PAGE_SIZE);
 
        if (cur_len)
                *cur_len = clen;
@@ -2080,24 +2106,79 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
        return num_pages;
 }
 
+static void
+cifs_uncached_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata)
+{
+       int i;
+       size_t bytes = wdata->bytes;
+
+       /* marshal up the pages into iov array */
+       for (i = 0; i < wdata->nr_pages; i++) {
+               iov[i + 1].iov_len = min_t(size_t, bytes, PAGE_SIZE);
+               iov[i + 1].iov_base = kmap(wdata->pages[i]);
+               bytes -= iov[i + 1].iov_len;
+       }
+}
+
+static void
+cifs_uncached_writev_complete(struct work_struct *work)
+{
+       int i;
+       struct cifs_writedata *wdata = container_of(work,
+                                       struct cifs_writedata, work);
+       struct inode *inode = wdata->cfile->dentry->d_inode;
+       struct cifsInodeInfo *cifsi = CIFS_I(inode);
+
+       spin_lock(&inode->i_lock);
+       cifs_update_eof(cifsi, wdata->offset, wdata->bytes);
+       if (cifsi->server_eof > inode->i_size)
+               i_size_write(inode, cifsi->server_eof);
+       spin_unlock(&inode->i_lock);
+
+       complete(&wdata->done);
+
+       if (wdata->result != -EAGAIN) {
+               for (i = 0; i < wdata->nr_pages; i++)
+                       put_page(wdata->pages[i]);
+       }
+
+       kref_put(&wdata->refcount, cifs_writedata_release);
+}
+
+/* attempt to send write to server, retry on any -EAGAIN errors */
+static int
+cifs_uncached_retry_writev(struct cifs_writedata *wdata)
+{
+       int rc;
+
+       do {
+               if (wdata->cfile->invalidHandle) {
+                       rc = cifs_reopen_file(wdata->cfile, false);
+                       if (rc != 0)
+                               continue;
+               }
+               rc = cifs_async_writev(wdata);
+       } while (rc == -EAGAIN);
+
+       return rc;
+}
+
 static ssize_t
 cifs_iovec_write(struct file *file, const struct iovec *iov,
                 unsigned long nr_segs, loff_t *poffset)
 {
-       unsigned int written;
-       unsigned long num_pages, npages, i;
+       unsigned long nr_pages, i;
        size_t copied, len, cur_len;
        ssize_t total_written = 0;
-       struct kvec *to_send;
-       struct page **pages;
+       loff_t offset = *poffset;
        struct iov_iter it;
-       struct inode *inode;
        struct cifsFileInfo *open_file;
-       struct cifs_tcon *pTcon;
+       struct cifs_tcon *tcon;
        struct cifs_sb_info *cifs_sb;
-       struct cifs_io_parms io_parms;
-       int xid, rc;
-       __u32 pid;
+       struct cifs_writedata *wdata, *tmp;
+       struct list_head wdata_list;
+       int rc;
+       pid_t pid;
 
        len = iov_length(iov, nr_segs);
        if (!len)
@@ -2107,103 +2188,103 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
        if (rc)
                return rc;
 
+       INIT_LIST_HEAD(&wdata_list);
        cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
-       num_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
-
-       pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL);
-       if (!pages)
-               return -ENOMEM;
-
-       to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL);
-       if (!to_send) {
-               kfree(pages);
-               return -ENOMEM;
-       }
-
-       rc = cifs_write_allocate_pages(pages, num_pages);
-       if (rc) {
-               kfree(pages);
-               kfree(to_send);
-               return rc;
-       }
-
-       xid = GetXid();
        open_file = file->private_data;
+       tcon = tlink_tcon(open_file->tlink);
 
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
                pid = open_file->pid;
        else
                pid = current->tgid;
 
-       pTcon = tlink_tcon(open_file->tlink);
-       inode = file->f_path.dentry->d_inode;
-
        iov_iter_init(&it, iov, nr_segs, len, 0);
-       npages = num_pages;
-
        do {
-               size_t save_len = cur_len;
-               for (i = 0; i < npages; i++) {
-                       copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
-                       copied = iov_iter_copy_from_user(pages[i], &it, 0,
-                                                        copied);
+               size_t save_len;
+
+               nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
+               wdata = cifs_writedata_alloc(nr_pages,
+                                            cifs_uncached_writev_complete);
+               if (!wdata) {
+                       rc = -ENOMEM;
+                       break;
+               }
+
+               rc = cifs_write_allocate_pages(wdata->pages, nr_pages);
+               if (rc) {
+                       kfree(wdata);
+                       break;
+               }
+
+               save_len = cur_len;
+               for (i = 0; i < nr_pages; i++) {
+                       copied = min_t(const size_t, cur_len, PAGE_SIZE);
+                       copied = iov_iter_copy_from_user(wdata->pages[i], &it,
+                                                        0, copied);
                        cur_len -= copied;
                        iov_iter_advance(&it, copied);
-                       to_send[i+1].iov_base = kmap(pages[i]);
-                       to_send[i+1].iov_len = copied;
                }
-
                cur_len = save_len - cur_len;
 
-               do {
-                       if (open_file->invalidHandle) {
-                               rc = cifs_reopen_file(open_file, false);
-                               if (rc != 0)
-                                       break;
-                       }
-                       io_parms.netfid = open_file->netfid;
-                       io_parms.pid = pid;
-                       io_parms.tcon = pTcon;
-                       io_parms.offset = *poffset;
-                       io_parms.length = cur_len;
-                       rc = CIFSSMBWrite2(xid, &io_parms, &written, to_send,
-                                          npages, 0);
-               } while (rc == -EAGAIN);
-
-               for (i = 0; i < npages; i++)
-                       kunmap(pages[i]);
-
-               if (written) {
-                       len -= written;
-                       total_written += written;
-                       cifs_update_eof(CIFS_I(inode), *poffset, written);
-                       *poffset += written;
-               } else if (rc < 0) {
-                       if (!total_written)
-                               total_written = rc;
+               wdata->sync_mode = WB_SYNC_ALL;
+               wdata->nr_pages = nr_pages;
+               wdata->offset = (__u64)offset;
+               wdata->cfile = cifsFileInfo_get(open_file);
+               wdata->pid = pid;
+               wdata->bytes = cur_len;
+               wdata->marshal_iov = cifs_uncached_marshal_iov;
+               rc = cifs_uncached_retry_writev(wdata);
+               if (rc) {
+                       kref_put(&wdata->refcount, cifs_writedata_release);
                        break;
                }
 
-               /* get length and number of kvecs of the next write */
-               npages = get_numpages(cifs_sb->wsize, len, &cur_len);
+               list_add_tail(&wdata->list, &wdata_list);
+               offset += cur_len;
+               len -= cur_len;
        } while (len > 0);
 
-       if (total_written > 0) {
-               spin_lock(&inode->i_lock);
-               if (*poffset > inode->i_size)
-                       i_size_write(inode, *poffset);
-               spin_unlock(&inode->i_lock);
+       /*
+        * If at least one write was successfully sent, then discard any rc
+        * value from the later writes. If the other write succeeds, then
+        * we'll end up returning whatever was written. If it fails, then
+        * we'll get a new rc value from that.
+        */
+       if (!list_empty(&wdata_list))
+               rc = 0;
+
+       /*
+        * Wait for and collect replies for any successful sends in order of
+        * increasing offset. Once an error is hit or we get a fatal signal
+        * while waiting, then return without waiting for any more replies.
+        */
+restart_loop:
+       list_for_each_entry_safe(wdata, tmp, &wdata_list, list) {
+               if (!rc) {
+                       /* FIXME: freezable too? */
+                       rc = wait_for_completion_killable(&wdata->done);
+                       if (rc)
+                               rc = -EINTR;
+                       else if (wdata->result)
+                               rc = wdata->result;
+                       else
+                               total_written += wdata->bytes;
+
+                       /* resend call if it's a retryable error */
+                       if (rc == -EAGAIN) {
+                               rc = cifs_uncached_retry_writev(wdata);
+                               goto restart_loop;
+                       }
+               }
+               list_del_init(&wdata->list);
+               kref_put(&wdata->refcount, cifs_writedata_release);
        }
 
-       cifs_stats_bytes_written(pTcon, total_written);
-       mark_inode_dirty_sync(inode);
+       if (total_written > 0)
+               *poffset += total_written;
 
-       for (i = 0; i < num_pages; i++)
-               put_page(pages[i]);
-       kfree(to_send);
-       kfree(pages);
-       FreeXid(xid);
-       return total_written;
+       cifs_stats_bytes_written(tcon, total_written);
+       return total_written ? total_written : (ssize_t)rc;
 }
 
 ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
index c273c12de98eb129f1bcb8f149dbfecb83032597..c29d1aa2c54f30a76c25aa6ba843008b567ca96f 100644 (file)
@@ -213,55 +213,62 @@ cifs_small_buf_release(void *buf_to_free)
 }
 
 /*
      Find a free multiplex id (SMB mid). Otherwise there could be
      mid collisions which might cause problems, demultiplexing the
      wrong response to this request. Multiplex ids could collide if
      one of a series requests takes much longer than the others, or
      if a very large number of long lived requests (byte range
      locks or FindNotify requests) are pending.  No more than
      64K-1 requests can be outstanding at one time.  If no
      mids are available, return zero.  A future optimization
      could make the combination of mids and uid the key we use
      to demultiplex on (rather than mid alone).
      In addition to the above check, the cifs demultiplex
      code already used the command code as a secondary
      check of the frame and if signing is negotiated the
      response would be discarded if the mid were the same
      but the signature was wrong.  Since the mid is not put in the
      pending queue until later (when it is about to be dispatched)
      we do have to limit the number of outstanding requests
      to somewhat less than 64K-1 although it is hard to imagine
      so many threads being in the vfs at one time.
-*/
-__u16 GetNextMid(struct TCP_Server_Info *server)
* Find a free multiplex id (SMB mid). Otherwise there could be
* mid collisions which might cause problems, demultiplexing the
* wrong response to this request. Multiplex ids could collide if
* one of a series requests takes much longer than the others, or
* if a very large number of long lived requests (byte range
* locks or FindNotify requests) are pending. No more than
* 64K-1 requests can be outstanding at one time. If no
* mids are available, return zero. A future optimization
* could make the combination of mids and uid the key we use
* to demultiplex on (rather than mid alone).
* In addition to the above check, the cifs demultiplex
* code already used the command code as a secondary
* check of the frame and if signing is negotiated the
* response would be discarded if the mid were the same
* but the signature was wrong. Since the mid is not put in the
* pending queue until later (when it is about to be dispatched)
* we do have to limit the number of outstanding requests
* to somewhat less than 64K-1 although it is hard to imagine
* so many threads being in the vfs at one time.
+ */
+__u64 GetNextMid(struct TCP_Server_Info *server)
 {
-       __u16 mid = 0;
-       __u16 last_mid;
+       __u64 mid = 0;
+       __u16 last_mid, cur_mid;
        bool collision;
 
        spin_lock(&GlobalMid_Lock);
-       last_mid = server->CurrentMid; /* we do not want to loop forever */
-       server->CurrentMid++;
-       /* This nested loop looks more expensive than it is.
-       In practice the list of pending requests is short,
-       fewer than 50, and the mids are likely to be unique
-       on the first pass through the loop unless some request
-       takes longer than the 64 thousand requests before it
-       (and it would also have to have been a request that
-        did not time out) */
-       while (server->CurrentMid != last_mid) {
+
+       /* mid is 16 bit only for CIFS/SMB */
+       cur_mid = (__u16)((server->CurrentMid) & 0xffff);
+       /* we do not want to loop forever */
+       last_mid = cur_mid;
+       cur_mid++;
+
+       /*
+        * This nested loop looks more expensive than it is.
+        * In practice the list of pending requests is short,
+        * fewer than 50, and the mids are likely to be unique
+        * on the first pass through the loop unless some request
+        * takes longer than the 64 thousand requests before it
+        * (and it would also have to have been a request that
+        * did not time out).
+        */
+       while (cur_mid != last_mid) {
                struct mid_q_entry *mid_entry;
                unsigned int num_mids;
 
                collision = false;
-               if (server->CurrentMid == 0)
-                       server->CurrentMid++;
+               if (cur_mid == 0)
+                       cur_mid++;
 
                num_mids = 0;
                list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
                        ++num_mids;
-                       if (mid_entry->mid == server->CurrentMid &&
-                           mid_entry->midState == MID_REQUEST_SUBMITTED) {
+                       if (mid_entry->mid == cur_mid &&
+                           mid_entry->mid_state == MID_REQUEST_SUBMITTED) {
                                /* This mid is in use, try a different one */
                                collision = true;
                                break;
@@ -282,10 +289,11 @@ __u16 GetNextMid(struct TCP_Server_Info *server)
                        server->tcpStatus = CifsNeedReconnect;
 
                if (!collision) {
-                       mid = server->CurrentMid;
+                       mid = (__u64)cur_mid;
+                       server->CurrentMid = mid;
                        break;
                }
-               server->CurrentMid++;
+               cur_mid++;
        }
        spin_unlock(&GlobalMid_Lock);
        return mid;
@@ -420,8 +428,10 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)
 }
 
 int
-checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read)
+checkSMB(char *buf, unsigned int total_read)
 {
+       struct smb_hdr *smb = (struct smb_hdr *)buf;
+       __u16 mid = smb->Mid;
        __u32 rfclen = be32_to_cpu(smb->smb_buf_length);
        __u32 clc_len;  /* calculated length */
        cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x",
@@ -502,8 +512,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int total_read)
 }
 
 bool
-is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
+is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
 {
+       struct smb_hdr *buf = (struct smb_hdr *)buffer;
        struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
        struct list_head *tmp, *tmp1, *tmp2;
        struct cifs_ses *ses;
@@ -584,7 +595,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 
                                cifs_set_oplock_level(pCifsInode,
                                        pSMB->OplockLevel ? OPLOCK_READ : 0);
-                               queue_work(system_nrt_wq,
+                               queue_work(cifsiod_wq,
                                           &netfile->oplock_break);
                                netfile->oplock_break_cancelled = false;
 
@@ -604,16 +615,15 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 }
 
 void
-dump_smb(struct smb_hdr *smb_buf, int smb_buf_length)
+dump_smb(void *buf, int smb_buf_length)
 {
        int i, j;
        char debug_line[17];
-       unsigned char *buffer;
+       unsigned char *buffer = buf;
 
        if (traceSMB == 0)
                return;
 
-       buffer = (unsigned char *) smb_buf;
        for (i = 0, j = 0; i < smb_buf_length; i++, j++) {
                if (i % 8 == 0) {
                        /* have reached the beginning of line */
index 73e47e84b61a4827fa13ed69a41e205be510234e..dd23a321bdda60b8eacf105bea12b8e2cce51b2f 100644 (file)
@@ -836,8 +836,9 @@ ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode)
 }
 
 int
-map_smb_to_linux_error(struct smb_hdr *smb, bool logErr)
+map_smb_to_linux_error(char *buf, bool logErr)
 {
+       struct smb_hdr *smb = (struct smb_hdr *)buf;
        unsigned int i;
        int rc = -EIO;  /* if transport error smb error may not be set */
        __u8 smberrclass;
index 310918b6fcb46ff8f252d2557dabf30e7ea0181b..0961336513d5334d99084eee82addeb1b582ffbf 100644 (file)
@@ -60,8 +60,8 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
                memset(temp, 0, sizeof(struct mid_q_entry));
                temp->mid = smb_buffer->Mid;    /* always LE */
                temp->pid = current->pid;
-               temp->command = smb_buffer->Command;
-               cFYI(1, "For smb_command %d", temp->command);
+               temp->command = cpu_to_le16(smb_buffer->Command);
+               cFYI(1, "For smb_command %d", smb_buffer->Command);
        /*      do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
                /* when mid allocated can be before when sent */
                temp->when_alloc = jiffies;
@@ -75,7 +75,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
        }
 
        atomic_inc(&midCount);
-       temp->midState = MID_REQUEST_ALLOCATED;
+       temp->mid_state = MID_REQUEST_ALLOCATED;
        return temp;
 }
 
@@ -85,9 +85,9 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
 #ifdef CONFIG_CIFS_STATS2
        unsigned long now;
 #endif
-       midEntry->midState = MID_FREE;
+       midEntry->mid_state = MID_FREE;
        atomic_dec(&midCount);
-       if (midEntry->largeBuf)
+       if (midEntry->large_buf)
                cifs_buf_release(midEntry->resp_buf);
        else
                cifs_small_buf_release(midEntry->resp_buf);
@@ -97,8 +97,8 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
           something is wrong, unless it is quite a slow link or server */
        if ((now - midEntry->when_alloc) > HZ) {
                if ((cifsFYI & CIFS_TIMER) &&
-                  (midEntry->command != SMB_COM_LOCKING_ANDX)) {
-                       printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %d",
+                   (midEntry->command != cpu_to_le16(SMB_COM_LOCKING_ANDX))) {
+                       printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %llu",
                               midEntry->command, midEntry->mid);
                        printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n",
                               now - midEntry->when_alloc,
@@ -126,11 +126,11 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
        int rc = 0;
        int i = 0;
        struct msghdr smb_msg;
-       struct smb_hdr *smb_buffer = iov[0].iov_base;
+       __be32 *buf_len = (__be32 *)(iov[0].iov_base);
        unsigned int len = iov[0].iov_len;
        unsigned int total_len;
        int first_vec = 0;
-       unsigned int smb_buf_length = be32_to_cpu(smb_buffer->smb_buf_length);
+       unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
        struct socket *ssocket = server->ssocket;
 
        if (ssocket == NULL)
@@ -150,7 +150,7 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
                total_len += iov[i].iov_len;
 
        cFYI(1, "Sending smb:  total_len %d", total_len);
-       dump_smb(smb_buffer, len);
+       dump_smb(iov[0].iov_base, len);
 
        i = 0;
        while (total_len) {
@@ -158,24 +158,24 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
                                    n_vec - first_vec, total_len);
                if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
                        i++;
-                       /* if blocking send we try 3 times, since each can block
-                          for 5 seconds. For nonblocking  we have to try more
-                          but wait increasing amounts of time allowing time for
-                          socket to clear.  The overall time we wait in either
-                          case to send on the socket is about 15 seconds.
-                          Similarly we wait for 15 seconds for
-                          a response from the server in SendReceive[2]
-                          for the server to send a response back for
-                          most types of requests (except SMB Write
-                          past end of file which can be slow, and
-                          blocking lock operations). NFS waits slightly longer
-                          than CIFS, but this can make it take longer for
-                          nonresponsive servers to be detected and 15 seconds
-                          is more than enough time for modern networks to
-                          send a packet.  In most cases if we fail to send
-                          after the retries we will kill the socket and
-                          reconnect which may clear the network problem.
-                       */
+                       /*
+                        * If blocking send we try 3 times, since each can block
+                        * for 5 seconds. For nonblocking  we have to try more
+                        * but wait increasing amounts of time allowing time for
+                        * socket to clear.  The overall time we wait in either
+                        * case to send on the socket is about 15 seconds.
+                        * Similarly we wait for 15 seconds for a response from
+                        * the server in SendReceive[2] for the server to send
+                        * a response back for most types of requests (except
+                        * SMB Write past end of file which can be slow, and
+                        * blocking lock operations). NFS waits slightly longer
+                        * than CIFS, but this can make it take longer for
+                        * nonresponsive servers to be detected and 15 seconds
+                        * is more than enough time for modern networks to
+                        * send a packet.  In most cases if we fail to send
+                        * after the retries we will kill the socket and
+                        * reconnect which may clear the network problem.
+                        */
                        if ((i >= 14) || (!server->noblocksnd && (i > 2))) {
                                cERROR(1, "sends on sock %p stuck for 15 seconds",
                                    ssocket);
@@ -235,9 +235,8 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
        else
                rc = 0;
 
-       /* Don't want to modify the buffer as a
-          side effect of this call. */
-       smb_buffer->smb_buf_length = cpu_to_be32(smb_buf_length);
+       /* Don't want to modify the buffer as a side effect of this call. */
+       *buf_len = cpu_to_be32(smb_buf_length);
 
        return rc;
 }
@@ -342,13 +341,40 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
        int error;
 
        error = wait_event_freezekillable(server->response_q,
-                                   midQ->midState != MID_REQUEST_SUBMITTED);
+                                   midQ->mid_state != MID_REQUEST_SUBMITTED);
        if (error < 0)
                return -ERESTARTSYS;
 
        return 0;
 }
 
+static int
+cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
+                        unsigned int nvec, struct mid_q_entry **ret_mid)
+{
+       int rc;
+       struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
+       struct mid_q_entry *mid;
+
+       /* enable signing if server requires it */
+       if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+               hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+       mid = AllocMidQEntry(hdr, server);
+       if (mid == NULL)
+               return -ENOMEM;
+
+       /* put it on the pending_mid_q */
+       spin_lock(&GlobalMid_Lock);
+       list_add_tail(&mid->qhead, &server->pending_mid_q);
+       spin_unlock(&GlobalMid_Lock);
+
+       rc = cifs_sign_smb2(iov, nvec, server, &mid->sequence_number);
+       if (rc)
+               delete_mid(mid);
+       *ret_mid = mid;
+       return rc;
+}
 
 /*
  * Send a SMB request and set the callback function in the mid to handle
@@ -361,40 +387,24 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
 {
        int rc;
        struct mid_q_entry *mid;
-       struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
 
        rc = wait_for_free_request(server, ignore_pend ? CIFS_ASYNC_OP : 0);
        if (rc)
                return rc;
 
-       /* enable signing if server requires it */
-       if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
-               hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
-
        mutex_lock(&server->srv_mutex);
-       mid = AllocMidQEntry(hdr, server);
-       if (mid == NULL) {
+       rc = cifs_setup_async_request(server, iov, nvec, &mid);
+       if (rc) {
                mutex_unlock(&server->srv_mutex);
                cifs_add_credits(server, 1);
                wake_up(&server->request_q);
-               return -ENOMEM;
-       }
-
-       /* put it on the pending_mid_q */
-       spin_lock(&GlobalMid_Lock);
-       list_add_tail(&mid->qhead, &server->pending_mid_q);
-       spin_unlock(&GlobalMid_Lock);
-
-       rc = cifs_sign_smb2(iov, nvec, server, &mid->sequence_number);
-       if (rc) {
-               mutex_unlock(&server->srv_mutex);
-               goto out_err;
+               return rc;
        }
 
        mid->receive = receive;
        mid->callback = callback;
        mid->callback_data = cbdata;
-       mid->midState = MID_REQUEST_SUBMITTED;
+       mid->mid_state = MID_REQUEST_SUBMITTED;
 
        cifs_in_send_inc(server);
        rc = smb_sendv(server, iov, nvec);
@@ -424,14 +434,14 @@ out_err:
  */
 int
 SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
-               struct smb_hdr *in_buf, int flags)
+                char *in_buf, int flags)
 {
        int rc;
        struct kvec iov[1];
        int resp_buf_type;
 
-       iov[0].iov_base = (char *)in_buf;
-       iov[0].iov_len = be32_to_cpu(in_buf->smb_buf_length) + 4;
+       iov[0].iov_base = in_buf;
+       iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
        flags |= CIFS_NO_RESP;
        rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags);
        cFYI(DBG2, "SendRcvNoRsp flags %d rc %d", flags, rc);
@@ -444,11 +454,11 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
 {
        int rc = 0;
 
-       cFYI(1, "%s: cmd=%d mid=%d state=%d", __func__, mid->command,
-               mid->mid, mid->midState);
+       cFYI(1, "%s: cmd=%d mid=%llu state=%d", __func__,
+            le16_to_cpu(mid->command), mid->mid, mid->mid_state);
 
        spin_lock(&GlobalMid_Lock);
-       switch (mid->midState) {
+       switch (mid->mid_state) {
        case MID_RESPONSE_RECEIVED:
                spin_unlock(&GlobalMid_Lock);
                return rc;
@@ -463,8 +473,8 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
                break;
        default:
                list_del_init(&mid->qhead);
-               cERROR(1, "%s: invalid mid state mid=%d state=%d", __func__,
-                       mid->mid, mid->midState);
+               cERROR(1, "%s: invalid mid state mid=%llu state=%d", __func__,
+                      mid->mid, mid->mid_state);
                rc = -EIO;
        }
        spin_unlock(&GlobalMid_Lock);
@@ -514,7 +524,7 @@ int
 cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
                   bool log_error)
 {
-       unsigned int len = be32_to_cpu(mid->resp_buf->smb_buf_length) + 4;
+       unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
 
        dump_smb(mid->resp_buf, min_t(u32, 92, len));
 
@@ -534,6 +544,24 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
        return map_smb_to_linux_error(mid->resp_buf, log_error);
 }
 
+static int
+cifs_setup_request(struct cifs_ses *ses, struct kvec *iov,
+                  unsigned int nvec, struct mid_q_entry **ret_mid)
+{
+       int rc;
+       struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
+       struct mid_q_entry *mid;
+
+       rc = allocate_mid(ses, hdr, &mid);
+       if (rc)
+               return rc;
+       rc = cifs_sign_smb2(iov, nvec, ses->server, &mid->sequence_number);
+       if (rc)
+               delete_mid(mid);
+       *ret_mid = mid;
+       return rc;
+}
+
 int
 SendReceive2(const unsigned int xid, struct cifs_ses *ses,
             struct kvec *iov, int n_vec, int *pRespBufType /* ret */,
@@ -542,55 +570,53 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
        int rc = 0;
        int long_op;
        struct mid_q_entry *midQ;
-       struct smb_hdr *in_buf = iov[0].iov_base;
+       char *buf = iov[0].iov_base;
 
        long_op = flags & CIFS_TIMEOUT_MASK;
 
        *pRespBufType = CIFS_NO_BUFFER;  /* no response buf yet */
 
        if ((ses == NULL) || (ses->server == NULL)) {
-               cifs_small_buf_release(in_buf);
+               cifs_small_buf_release(buf);
                cERROR(1, "Null session");
                return -EIO;
        }
 
        if (ses->server->tcpStatus == CifsExiting) {
-               cifs_small_buf_release(in_buf);
+               cifs_small_buf_release(buf);
                return -ENOENT;
        }
 
-       /* Ensure that we do not send more than 50 overlapping requests
-          to the same server. We may make this configurable later or
-          use ses->maxReq */
+       /*
+        * Ensure that we do not send more than 50 overlapping requests
+        * to the same server. We may make this configurable later or
+        * use ses->maxReq.
+        */
 
        rc = wait_for_free_request(ses->server, long_op);
        if (rc) {
-               cifs_small_buf_release(in_buf);
+               cifs_small_buf_release(buf);
                return rc;
        }
 
-       /* make sure that we sign in the same order that we send on this socket
-          and avoid races inside tcp sendmsg code that could cause corruption
-          of smb data */
+       /*
+        * Make sure that we sign in the same order that we send on this socket
+        * and avoid races inside tcp sendmsg code that could cause corruption
+        * of smb data.
+        */
 
        mutex_lock(&ses->server->srv_mutex);
 
-       rc = allocate_mid(ses, in_buf, &midQ);
+       rc = cifs_setup_request(ses, iov, n_vec, &midQ);
        if (rc) {
                mutex_unlock(&ses->server->srv_mutex);
-               cifs_small_buf_release(in_buf);
+               cifs_small_buf_release(buf);
                /* Update # of requests on wire to server */
                cifs_add_credits(ses->server, 1);
                return rc;
        }
-       rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);
-       if (rc) {
-               mutex_unlock(&ses->server->srv_mutex);
-               cifs_small_buf_release(in_buf);
-               goto out;
-       }
 
-       midQ->midState = MID_REQUEST_SUBMITTED;
+       midQ->mid_state = MID_REQUEST_SUBMITTED;
        cifs_in_send_inc(ses->server);
        rc = smb_sendv(ses->server, iov, n_vec);
        cifs_in_send_dec(ses->server);
@@ -599,30 +625,30 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
        mutex_unlock(&ses->server->srv_mutex);
 
        if (rc < 0) {
-               cifs_small_buf_release(in_buf);
+               cifs_small_buf_release(buf);
                goto out;
        }
 
        if (long_op == CIFS_ASYNC_OP) {
-               cifs_small_buf_release(in_buf);
+               cifs_small_buf_release(buf);
                goto out;
        }
 
        rc = wait_for_response(ses->server, midQ);
        if (rc != 0) {
-               send_nt_cancel(ses->server, in_buf, midQ);
+               send_nt_cancel(ses->server, (struct smb_hdr *)buf, midQ);
                spin_lock(&GlobalMid_Lock);
-               if (midQ->midState == MID_REQUEST_SUBMITTED) {
+               if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
                        midQ->callback = DeleteMidQEntry;
                        spin_unlock(&GlobalMid_Lock);
-                       cifs_small_buf_release(in_buf);
+                       cifs_small_buf_release(buf);
                        cifs_add_credits(ses->server, 1);
                        return rc;
                }
                spin_unlock(&GlobalMid_Lock);
        }
 
-       cifs_small_buf_release(in_buf);
+       cifs_small_buf_release(buf);
 
        rc = cifs_sync_mid_result(midQ, ses->server);
        if (rc != 0) {
@@ -630,15 +656,16 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
                return rc;
        }
 
-       if (!midQ->resp_buf || midQ->midState != MID_RESPONSE_RECEIVED) {
+       if (!midQ->resp_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) {
                rc = -EIO;
                cFYI(1, "Bad MID state?");
                goto out;
        }
 
-       iov[0].iov_base = (char *)midQ->resp_buf;
-       iov[0].iov_len = be32_to_cpu(midQ->resp_buf->smb_buf_length) + 4;
-       if (midQ->largeBuf)
+       buf = (char *)midQ->resp_buf;
+       iov[0].iov_base = buf;
+       iov[0].iov_len = get_rfc1002_length(buf) + 4;
+       if (midQ->large_buf)
                *pRespBufType = CIFS_LARGE_BUFFER;
        else
                *pRespBufType = CIFS_SMALL_BUFFER;
@@ -710,7 +737,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
                goto out;
        }
 
-       midQ->midState = MID_REQUEST_SUBMITTED;
+       midQ->mid_state = MID_REQUEST_SUBMITTED;
 
        cifs_in_send_inc(ses->server);
        rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
@@ -728,7 +755,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
        if (rc != 0) {
                send_nt_cancel(ses->server, in_buf, midQ);
                spin_lock(&GlobalMid_Lock);
-               if (midQ->midState == MID_REQUEST_SUBMITTED) {
+               if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
                        /* no longer considered to be "in-flight" */
                        midQ->callback = DeleteMidQEntry;
                        spin_unlock(&GlobalMid_Lock);
@@ -745,13 +772,13 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
        }
 
        if (!midQ->resp_buf || !out_buf ||
-           midQ->midState != MID_RESPONSE_RECEIVED) {
+           midQ->mid_state != MID_RESPONSE_RECEIVED) {
                rc = -EIO;
                cERROR(1, "Bad MID state?");
                goto out;
        }
 
-       *pbytes_returned = be32_to_cpu(midQ->resp_buf->smb_buf_length);
+       *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
        memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
        rc = cifs_check_receive(midQ, ses->server, 0);
 out:
@@ -844,7 +871,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
                return rc;
        }
 
-       midQ->midState = MID_REQUEST_SUBMITTED;
+       midQ->mid_state = MID_REQUEST_SUBMITTED;
        cifs_in_send_inc(ses->server);
        rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
        cifs_in_send_dec(ses->server);
@@ -858,13 +885,13 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
 
        /* Wait for a reply - allow signals to interrupt. */
        rc = wait_event_interruptible(ses->server->response_q,
-               (!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
+               (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) ||
                ((ses->server->tcpStatus != CifsGood) &&
                 (ses->server->tcpStatus != CifsNew)));
 
        /* Were we interrupted by a signal ? */
        if ((rc == -ERESTARTSYS) &&
-               (midQ->midState == MID_REQUEST_SUBMITTED) &&
+               (midQ->mid_state == MID_REQUEST_SUBMITTED) &&
                ((ses->server->tcpStatus == CifsGood) ||
                 (ses->server->tcpStatus == CifsNew))) {
 
@@ -894,7 +921,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
                if (rc) {
                        send_nt_cancel(ses->server, in_buf, midQ);
                        spin_lock(&GlobalMid_Lock);
-                       if (midQ->midState == MID_REQUEST_SUBMITTED) {
+                       if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
                                /* no longer considered to be "in-flight" */
                                midQ->callback = DeleteMidQEntry;
                                spin_unlock(&GlobalMid_Lock);
@@ -912,13 +939,13 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
                return rc;
 
        /* rcvd frame is ok */
-       if (out_buf == NULL || midQ->midState != MID_RESPONSE_RECEIVED) {
+       if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) {
                rc = -EIO;
                cERROR(1, "Bad MID state?");
                goto out;
        }
 
-       *pbytes_returned = be32_to_cpu(midQ->resp_buf->smb_buf_length);
+       *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
        memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
        rc = cifs_check_receive(midQ, ses->server, 0);
 out:
index 926d02068a142c6ac4b5ac4cb7f02fec0b666937..922f146e42354fb34d7ed68f225901b44e5db77d 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
index 404111b016c93324535b4551dd2239f35c398966..2b60ce1996aa2ea63f38b4f4d1990f0dd5a7a916 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/jffs2.h>
 #include <linux/mtd/mtd.h>
@@ -42,12 +44,13 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
 
        tsk = kthread_run(jffs2_garbage_collect_thread, c, "jffs2_gcd_mtd%d", c->mtd->index);
        if (IS_ERR(tsk)) {
-               printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %ld\n", -PTR_ERR(tsk));
+               pr_warn("fork failed for JFFS2 garbage collect thread: %ld\n",
+                       -PTR_ERR(tsk));
                complete(&c->gc_thread_exit);
                ret = PTR_ERR(tsk);
        } else {
                /* Wait for it... */
-               D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", tsk->pid));
+               jffs2_dbg(1, "Garbage collect thread is pid %d\n", tsk->pid);
                wait_for_completion(&c->gc_thread_start);
                ret = tsk->pid;
        }
@@ -60,7 +63,7 @@ void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
        int wait = 0;
        spin_lock(&c->erase_completion_lock);
        if (c->gc_task) {
-               D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
+               jffs2_dbg(1, "Killing GC task %d\n", c->gc_task->pid);
                send_sig(SIGKILL, c->gc_task, 1);
                wait = 1;
        }
@@ -90,7 +93,7 @@ static int jffs2_garbage_collect_thread(void *_c)
                if (!jffs2_thread_should_wake(c)) {
                        set_current_state (TASK_INTERRUPTIBLE);
                        spin_unlock(&c->erase_completion_lock);
-                       D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
+                       jffs2_dbg(1, "%s(): sleeping...\n", __func__);
                        schedule();
                } else
                        spin_unlock(&c->erase_completion_lock);
@@ -109,7 +112,7 @@ static int jffs2_garbage_collect_thread(void *_c)
                schedule_timeout_interruptible(msecs_to_jiffies(50));
 
                if (kthread_should_stop()) {
-                       D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread():  kthread_stop() called.\n"));
+                       jffs2_dbg(1, "%s(): kthread_stop() called\n", __func__);
                        goto die;
                }
 
@@ -126,28 +129,32 @@ static int jffs2_garbage_collect_thread(void *_c)
 
                        switch(signr) {
                        case SIGSTOP:
-                               D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n"));
+                               jffs2_dbg(1, "%s(): SIGSTOP received\n",
+                                         __func__);
                                set_current_state(TASK_STOPPED);
                                schedule();
                                break;
 
                        case SIGKILL:
-                               D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
+                               jffs2_dbg(1, "%s(): SIGKILL received\n",
+                                         __func__);
                                goto die;
 
                        case SIGHUP:
-                               D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n"));
+                               jffs2_dbg(1, "%s(): SIGHUP received\n",
+                                         __func__);
                                break;
                        default:
-                               D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr));
+                               jffs2_dbg(1, "%s(): signal %ld received\n",
+                                         __func__, signr);
                        }
                }
                /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */
                disallow_signal(SIGHUP);
 
-               D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n"));
+               jffs2_dbg(1, "%s(): pass\n", __func__);
                if (jffs2_garbage_collect_pass(c) == -ENOSPC) {
-                       printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n");
+                       pr_notice("No space for garbage collection. Aborting GC thread\n");
                        goto die;
                }
        }
index 3005ec4520adf88314af781a956bae7ab1f04b22..a3750f902adcbae8f8cabbad066f9d629b805155 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -307,8 +309,8 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
           trying to GC to make more space. It'll be a fruitless task */
        c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
 
-       dbg_fsbuild("JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
-                 c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks);
+       dbg_fsbuild("trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
+                   c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks);
        dbg_fsbuild("Blocks required to allow deletion:    %d (%d KiB)\n",
                  c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024);
        dbg_fsbuild("Blocks required to allow writes:      %d (%d KiB)\n",
index 96ed3c9ec3fc3b3a52b521b1227a1e561fe0c3d9..4849a4c9a0e24f1b8b4202630d68e491eac113dc 100644 (file)
@@ -12,6 +12,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include "compr.h"
 
 static DEFINE_SPINLOCK(jffs2_compressor_list_lock);
@@ -79,7 +81,7 @@ static int jffs2_selected_compress(u8 compr, unsigned char *data_in,
 
        output_buf = kmalloc(*cdatalen, GFP_KERNEL);
        if (!output_buf) {
-               printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
+               pr_warn("No memory for compressor allocation. Compression failed.\n");
                return ret;
        }
        orig_slen = *datalen;
@@ -188,7 +190,8 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                                tmp_buf = kmalloc(orig_slen, GFP_KERNEL);
                                spin_lock(&jffs2_compressor_list_lock);
                                if (!tmp_buf) {
-                                       printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n", orig_slen);
+                                       pr_warn("No memory for compressor allocation. (%d bytes)\n",
+                                               orig_slen);
                                        continue;
                                }
                                else {
@@ -235,7 +238,7 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                                cpage_out, datalen, cdatalen);
                break;
        default:
-               printk(KERN_ERR "JFFS2: unknown compression mode.\n");
+               pr_err("unknown compression mode\n");
        }
 
        if (ret == JFFS2_COMPR_NONE) {
@@ -277,7 +280,8 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen);
                                spin_lock(&jffs2_compressor_list_lock);
                                if (ret) {
-                                       printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
+                                       pr_warn("Decompressor \"%s\" returned %d\n",
+                                               this->name, ret);
                                }
                                else {
                                        this->stat_decompr_blocks++;
@@ -287,7 +291,7 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                                return ret;
                        }
                }
-               printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype);
+               pr_warn("compression type 0x%02x not available\n", comprtype);
                spin_unlock(&jffs2_compressor_list_lock);
                return -EIO;
        }
@@ -299,7 +303,7 @@ int jffs2_register_compressor(struct jffs2_compressor *comp)
        struct jffs2_compressor *this;
 
        if (!comp->name) {
-               printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
+               pr_warn("NULL compressor name at registering JFFS2 compressor. Failed.\n");
                return -1;
        }
        comp->compr_buf_size=0;
@@ -309,7 +313,7 @@ int jffs2_register_compressor(struct jffs2_compressor *comp)
        comp->stat_compr_new_size=0;
        comp->stat_compr_blocks=0;
        comp->stat_decompr_blocks=0;
-       D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
+       jffs2_dbg(1, "Registering JFFS2 compressor \"%s\"\n", comp->name);
 
        spin_lock(&jffs2_compressor_list_lock);
 
@@ -332,15 +336,15 @@ out:
 
 int jffs2_unregister_compressor(struct jffs2_compressor *comp)
 {
-       D2(struct jffs2_compressor *this;)
+       D2(struct jffs2_compressor *this);
 
-       D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
+       jffs2_dbg(1, "Unregistering JFFS2 compressor \"%s\"\n", comp->name);
 
        spin_lock(&jffs2_compressor_list_lock);
 
        if (comp->usecount) {
                spin_unlock(&jffs2_compressor_list_lock);
-               printk(KERN_WARNING "JFFS2: Compressor module is in use. Unregister failed.\n");
+               pr_warn("Compressor module is in use. Unregister failed.\n");
                return -1;
        }
        list_del(&comp->list);
@@ -377,17 +381,17 @@ int __init jffs2_compressors_init(void)
 /* Setting default compression mode */
 #ifdef CONFIG_JFFS2_CMODE_NONE
        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
-       D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
+       jffs2_dbg(1, "default compression mode: none\n");
 #else
 #ifdef CONFIG_JFFS2_CMODE_SIZE
        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
-       D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
+       jffs2_dbg(1, "default compression mode: size\n");
 #else
 #ifdef CONFIG_JFFS2_CMODE_FAVOURLZO
        jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO;
-       D1(printk(KERN_INFO "JFFS2: default compression mode: favourlzo\n");)
+       jffs2_dbg(1, "default compression mode: favourlzo\n");
 #else
-       D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
+       jffs2_dbg(1, "default compression mode: priority\n");
 #endif
 #endif
 #endif
index af186ee674d8f79ddda691a86e59af88fa29bd24..c553bd6506da228086da2939832d3b63fb770463 100644 (file)
@@ -33,7 +33,6 @@ static int __init alloc_workspace(void)
        lzo_compress_buf = vmalloc(lzo1x_worst_compress(PAGE_SIZE));
 
        if (!lzo_mem || !lzo_compress_buf) {
-               printk(KERN_WARNING "Failed to allocate lzo deflate workspace\n");
                free_workspace();
                return -ENOMEM;
        }
index 9e7cec808c4c97c7f1530b10c2f16fd512464ea6..92e0644bf8673d91c091edf437e5a8e5e2941d64 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/jffs2.h>
index 5a001020c542a652726e18521429d2d44b6c31d5..0b9a1e44e833e691e2fc8dd2ca3135274113aad3 100644 (file)
@@ -14,6 +14,8 @@
 #error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
 #endif
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/zlib.h>
 #include <linux/zutil.h>
@@ -42,18 +44,18 @@ static int __init alloc_workspaces(void)
 {
        def_strm.workspace = vmalloc(zlib_deflate_workspacesize(MAX_WBITS,
                                                        MAX_MEM_LEVEL));
-       if (!def_strm.workspace) {
-               printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL));
+       if (!def_strm.workspace)
                return -ENOMEM;
-       }
-       D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL)));
+
+       jffs2_dbg(1, "Allocated %d bytes for deflate workspace\n",
+                 zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL));
        inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
        if (!inf_strm.workspace) {
-               printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
                vfree(def_strm.workspace);
                return -ENOMEM;
        }
-       D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
+       jffs2_dbg(1, "Allocated %d bytes for inflate workspace\n",
+                 zlib_inflate_workspacesize());
        return 0;
 }
 
@@ -79,7 +81,7 @@ static int jffs2_zlib_compress(unsigned char *data_in,
        mutex_lock(&deflate_mutex);
 
        if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
-               printk(KERN_WARNING "deflateInit failed\n");
+               pr_warn("deflateInit failed\n");
                mutex_unlock(&deflate_mutex);
                return -1;
        }
@@ -93,13 +95,14 @@ static int jffs2_zlib_compress(unsigned char *data_in,
        while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) {
                def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE);
                def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out);
-               D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
-                         def_strm.avail_in, def_strm.avail_out));
+               jffs2_dbg(1, "calling deflate with avail_in %d, avail_out %d\n",
+                         def_strm.avail_in, def_strm.avail_out);
                ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
-               D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n",
-                         def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out));
+               jffs2_dbg(1, "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n",
+                         def_strm.avail_in, def_strm.avail_out,
+                         def_strm.total_in, def_strm.total_out);
                if (ret != Z_OK) {
-                       D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
+                       jffs2_dbg(1, "deflate in loop returned %d\n", ret);
                        zlib_deflateEnd(&def_strm);
                        mutex_unlock(&deflate_mutex);
                        return -1;
@@ -111,20 +114,20 @@ static int jffs2_zlib_compress(unsigned char *data_in,
        zlib_deflateEnd(&def_strm);
 
        if (ret != Z_STREAM_END) {
-               D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
+               jffs2_dbg(1, "final deflate returned %d\n", ret);
                ret = -1;
                goto out;
        }
 
        if (def_strm.total_out >= def_strm.total_in) {
-               D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n",
-                         def_strm.total_in, def_strm.total_out));
+               jffs2_dbg(1, "zlib compressed %ld bytes into %ld; failing\n",
+                         def_strm.total_in, def_strm.total_out);
                ret = -1;
                goto out;
        }
 
-       D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
-                 def_strm.total_in, def_strm.total_out));
+       jffs2_dbg(1, "zlib compressed %ld bytes into %ld\n",
+                 def_strm.total_in, def_strm.total_out);
 
        *dstlen = def_strm.total_out;
        *sourcelen = def_strm.total_in;
@@ -157,18 +160,18 @@ static int jffs2_zlib_decompress(unsigned char *data_in,
            ((data_in[0] & 0x0f) == Z_DEFLATED) &&
            !(((data_in[0]<<8) + data_in[1]) % 31)) {
 
-               D2(printk(KERN_DEBUG "inflate skipping adler32\n"));
+               jffs2_dbg(2, "inflate skipping adler32\n");
                wbits = -((data_in[0] >> 4) + 8);
                inf_strm.next_in += 2;
                inf_strm.avail_in -= 2;
        } else {
                /* Let this remain D1 for now -- it should never happen */
-               D1(printk(KERN_DEBUG "inflate not skipping adler32\n"));
+               jffs2_dbg(1, "inflate not skipping adler32\n");
        }
 
 
        if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
-               printk(KERN_WARNING "inflateInit failed\n");
+               pr_warn("inflateInit failed\n");
                mutex_unlock(&inflate_mutex);
                return 1;
        }
@@ -176,7 +179,7 @@ static int jffs2_zlib_decompress(unsigned char *data_in,
        while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
                ;
        if (ret != Z_STREAM_END) {
-               printk(KERN_NOTICE "inflate returned %d\n", ret);
+               pr_notice("inflate returned %d\n", ret);
        }
        zlib_inflateEnd(&inf_strm);
        mutex_unlock(&inflate_mutex);
index e0b76c87a91af298712a5ebc06b3ac7e8a953238..1090eb64b90d66dac39e32677edae8f32ddde02f 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/pagemap.h>
@@ -261,12 +263,15 @@ void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c)
                bad += c->sector_size;
        }
 
-#define check(sz) \
-       if (sz != c->sz##_size) {                       \
-               printk(KERN_WARNING #sz "_size mismatch counted 0x%x, c->" #sz "_size 0x%x\n", \
-                      sz, c->sz##_size);               \
-               dump = 1;                               \
-       }
+#define check(sz)                                                      \
+do {                                                                   \
+       if (sz != c->sz##_size) {                                       \
+               pr_warn("%s_size mismatch counted 0x%x, c->%s_size 0x%x\n", \
+                       #sz, sz, #sz, c->sz##_size);                    \
+               dump = 1;                                               \
+       }                                                               \
+} while (0)
+
        check(free);
        check(dirty);
        check(used);
@@ -274,11 +279,12 @@ void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c)
        check(unchecked);
        check(bad);
        check(erasing);
+
 #undef check
 
        if (nr_counted != c->nr_blocks) {
-               printk(KERN_WARNING "%s counted only 0x%x blocks of 0x%x. Where are the others?\n",
-                      __func__, nr_counted, c->nr_blocks);
+               pr_warn("%s counted only 0x%x blocks of 0x%x. Where are the others?\n",
+                       __func__, nr_counted, c->nr_blocks);
                dump = 1;
        }
 
index c4f8eef5ca68b79d82aee1314166ce61802a90b7..4fd9be4cbc984a56b6b8bbb85c3d97d8c8a3166c 100644 (file)
@@ -51,6 +51,7 @@
  * superseded by nicer dbg_xxx() macros...
  */
 #if CONFIG_JFFS2_FS_DEBUG > 0
+#define DEBUG
 #define D1(x) x
 #else
 #define D1(x)
 #define D2(x)
 #endif
 
+#define jffs2_dbg(level, fmt, ...)             \
+do {                                           \
+       if (CONFIG_JFFS2_FS_DEBUG >= level)     \
+               pr_debug(fmt, ##__VA_ARGS__);   \
+} while (0)
+
 /* The prefixes of JFFS2 messages */
+#define JFFS2_DBG              KERN_DEBUG
 #define JFFS2_DBG_PREFIX       "[JFFS2 DBG]"
-#define JFFS2_ERR_PREFIX       "JFFS2 error:"
-#define JFFS2_WARN_PREFIX      "JFFS2 warning:"
-#define JFFS2_NOTICE_PREFIX    "JFFS2 notice:"
-
-#define JFFS2_ERR      KERN_ERR
-#define JFFS2_WARN     KERN_WARNING
-#define JFFS2_NOT      KERN_NOTICE
-#define JFFS2_DBG      KERN_DEBUG
-
 #define JFFS2_DBG_MSG_PREFIX   JFFS2_DBG JFFS2_DBG_PREFIX
-#define JFFS2_ERR_MSG_PREFIX   JFFS2_ERR JFFS2_ERR_PREFIX
-#define JFFS2_WARN_MSG_PREFIX  JFFS2_WARN JFFS2_WARN_PREFIX
-#define JFFS2_NOTICE_MSG_PREFIX        JFFS2_NOT JFFS2_NOTICE_PREFIX
 
 /* JFFS2 message macros */
-#define JFFS2_ERROR(fmt, ...)                                          \
-       do {                                                            \
-               printk(JFFS2_ERR_MSG_PREFIX                             \
-                       " (%d) %s: " fmt, task_pid_nr(current),         \
-                       __func__ , ##__VA_ARGS__);                      \
-       } while(0)
+#define JFFS2_ERROR(fmt, ...)                                  \
+       pr_err("error: (%d) %s: " fmt,                          \
+              task_pid_nr(current), __func__, ##__VA_ARGS__)
 
 #define JFFS2_WARNING(fmt, ...)                                                \
-       do {                                                            \
-               printk(JFFS2_WARN_MSG_PREFIX                            \
-                       " (%d) %s: " fmt, task_pid_nr(current),         \
-                       __func__ , ##__VA_ARGS__);                      \
-       } while(0)
+       pr_warn("warning: (%d) %s: " fmt,                               \
+               task_pid_nr(current), __func__, ##__VA_ARGS__)
 
 #define JFFS2_NOTICE(fmt, ...)                                         \
-       do {                                                            \
-               printk(JFFS2_NOTICE_MSG_PREFIX                          \
-                       " (%d) %s: " fmt, task_pid_nr(current),         \
-                       __func__ , ##__VA_ARGS__);                      \
-       } while(0)
+       pr_notice("notice: (%d) %s: " fmt,                              \
+                 task_pid_nr(current), __func__, ##__VA_ARGS__)
 
 #define JFFS2_DEBUG(fmt, ...)                                          \
-       do {                                                            \
-               printk(JFFS2_DBG_MSG_PREFIX                             \
-                       " (%d) %s: " fmt, task_pid_nr(current),         \
-                       __func__ , ##__VA_ARGS__);                      \
-       } while(0)
+       printk(KERN_DEBUG "[JFFS2 DBG] (%d) %s: " fmt,                  \
+              task_pid_nr(current), __func__, ##__VA_ARGS__)
 
 /*
  * We split our debugging messages on several parts, depending on the JFFS2
index 973ac5822bd7814cd8f4e6d43ae31c2a3b3f6d84..b56018896d5e398b3f5bce94776db8dd0cf9de37 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
@@ -79,7 +81,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
        uint32_t ino = 0;
        struct inode *inode = NULL;
 
-       D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
+       jffs2_dbg(1, "jffs2_lookup()\n");
 
        if (target->d_name.len > JFFS2_MAX_NAME_LEN)
                return ERR_PTR(-ENAMETOOLONG);
@@ -103,7 +105,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
        if (ino) {
                inode = jffs2_iget(dir_i->i_sb, ino);
                if (IS_ERR(inode))
-                       printk(KERN_WARNING "iget() failed for ino #%u\n", ino);
+                       pr_warn("iget() failed for ino #%u\n", ino);
        }
 
        return d_splice_alias(inode, target);
@@ -119,21 +121,22 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
        struct jffs2_full_dirent *fd;
        unsigned long offset, curofs;
 
-       D1(printk(KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", filp->f_path.dentry->d_inode->i_ino));
+       jffs2_dbg(1, "jffs2_readdir() for dir_i #%lu\n",
+                 filp->f_path.dentry->d_inode->i_ino);
 
        f = JFFS2_INODE_INFO(inode);
 
        offset = filp->f_pos;
 
        if (offset == 0) {
-               D1(printk(KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino));
+               jffs2_dbg(1, "Dirent 0: \".\", ino #%lu\n", inode->i_ino);
                if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
                        goto out;
                offset++;
        }
        if (offset == 1) {
                unsigned long pino = parent_ino(filp->f_path.dentry);
-               D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino));
+               jffs2_dbg(1, "Dirent 1: \"..\", ino #%lu\n", pino);
                if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0)
                        goto out;
                offset++;
@@ -146,16 +149,18 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
                curofs++;
                /* First loop: curofs = 2; offset = 2 */
                if (curofs < offset) {
-                       D2(printk(KERN_DEBUG "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n",
-                                 fd->name, fd->ino, fd->type, curofs, offset));
+                       jffs2_dbg(2, "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n",
+                                 fd->name, fd->ino, fd->type, curofs, offset);
                        continue;
                }
                if (!fd->ino) {
-                       D2(printk(KERN_DEBUG "Skipping deletion dirent \"%s\"\n", fd->name));
+                       jffs2_dbg(2, "Skipping deletion dirent \"%s\"\n",
+                                 fd->name);
                        offset++;
                        continue;
                }
-               D2(printk(KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, fd->name, fd->ino, fd->type));
+               jffs2_dbg(2, "Dirent %ld: \"%s\", ino #%u, type %d\n",
+                         offset, fd->name, fd->ino, fd->type);
                if (filldir(dirent, fd->name, strlen(fd->name), offset, fd->ino, fd->type) < 0)
                        break;
                offset++;
@@ -184,12 +189,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry,
 
        c = JFFS2_SB_INFO(dir_i->i_sb);
 
-       D1(printk(KERN_DEBUG "jffs2_create()\n"));
+       jffs2_dbg(1, "%s()\n", __func__);
 
        inode = jffs2_new_inode(dir_i, mode, ri);
 
        if (IS_ERR(inode)) {
-               D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
+               jffs2_dbg(1, "jffs2_new_inode() failed\n");
                jffs2_free_raw_inode(ri);
                return PTR_ERR(inode);
        }
@@ -217,9 +222,9 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry,
 
        jffs2_free_raw_inode(ri);
 
-       D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
-                 inode->i_ino, inode->i_mode, inode->i_nlink,
-                 f->inocache->pino_nlink, inode->i_mapping->nrpages));
+       jffs2_dbg(1, "%s(): Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
+                 __func__, inode->i_ino, inode->i_mode, inode->i_nlink,
+                 f->inocache->pino_nlink, inode->i_mapping->nrpages);
 
        d_instantiate(dentry, inode);
        unlock_new_inode(inode);
@@ -362,14 +367,15 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
        /* We use f->target field to store the target path. */
        f->target = kmemdup(target, targetlen + 1, GFP_KERNEL);
        if (!f->target) {
-               printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
+               pr_warn("Can't allocate %d bytes of memory\n", targetlen + 1);
                mutex_unlock(&f->sem);
                jffs2_complete_reservation(c);
                ret = -ENOMEM;
                goto fail;
        }
 
-       D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target));
+       jffs2_dbg(1, "%s(): symlink's target '%s' cached\n",
+                 __func__, (char *)f->target);
 
        /* No data here. Only a metadata node, which will be
           obsoleted by the first data write
@@ -856,7 +862,8 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
                        f->inocache->pino_nlink++;
                mutex_unlock(&f->sem);
 
-               printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
+               pr_notice("%s(): Link succeeded, unlink failed (err %d). You now have a hard link\n",
+                         __func__, ret);
                /* Might as well let the VFS know */
                d_instantiate(new_dentry, old_dentry->d_inode);
                ihold(old_dentry->d_inode);
index eafb8d37a6fb89173d26ecf527a3f69433a2565f..4a6cf289be248cbfaedc60dea23afc84fcbbbf44 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
@@ -46,11 +48,12 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
 #else /* Linux */
        struct erase_info *instr;
 
-       D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#08x (range %#08x-%#08x)\n",
-                               jeb->offset, jeb->offset, jeb->offset + c->sector_size));
+       jffs2_dbg(1, "%s(): erase block %#08x (range %#08x-%#08x)\n",
+                 __func__,
+                 jeb->offset, jeb->offset, jeb->offset + c->sector_size);
        instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
        if (!instr) {
-               printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
+               pr_warn("kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
                mutex_lock(&c->erase_free_sem);
                spin_lock(&c->erase_completion_lock);
                list_move(&jeb->list, &c->erase_pending_list);
@@ -69,7 +72,6 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
        instr->len = c->sector_size;
        instr->callback = jffs2_erase_callback;
        instr->priv = (unsigned long)(&instr[1]);
-       instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
 
        ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
        ((struct erase_priv_struct *)instr->priv)->c = c;
@@ -84,7 +86,8 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
 
        if (ret == -ENOMEM || ret == -EAGAIN) {
                /* Erase failed immediately. Refile it on the list */
-               D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
+               jffs2_dbg(1, "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n",
+                         jeb->offset, ret);
                mutex_lock(&c->erase_free_sem);
                spin_lock(&c->erase_completion_lock);
                list_move(&jeb->list, &c->erase_pending_list);
@@ -97,9 +100,11 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
        }
 
        if (ret == -EROFS)
-               printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset);
+               pr_warn("Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n",
+                       jeb->offset);
        else
-               printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
+               pr_warn("Erase at 0x%08x failed immediately: errno %d\n",
+                       jeb->offset, ret);
 
        jffs2_erase_failed(c, jeb, bad_offset);
 }
@@ -125,13 +130,14 @@ int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
 
                        work_done++;
                        if (!--count) {
-                               D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
+                               jffs2_dbg(1, "Count reached. jffs2_erase_pending_blocks leaving\n");
                                goto done;
                        }
 
                } else if (!list_empty(&c->erase_pending_list)) {
                        jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
-                       D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+                       jffs2_dbg(1, "Starting erase of pending block 0x%08x\n",
+                                 jeb->offset);
                        list_del(&jeb->list);
                        c->erasing_size += c->sector_size;
                        c->wasted_size -= jeb->wasted_size;
@@ -159,13 +165,13 @@ int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
        spin_unlock(&c->erase_completion_lock);
        mutex_unlock(&c->erase_free_sem);
  done:
-       D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
+       jffs2_dbg(1, "jffs2_erase_pending_blocks completed\n");
        return work_done;
 }
 
 static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
-       D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
+       jffs2_dbg(1, "Erase completed successfully at 0x%08x\n", jeb->offset);
        mutex_lock(&c->erase_free_sem);
        spin_lock(&c->erase_completion_lock);
        list_move_tail(&jeb->list, &c->erase_complete_list);
@@ -214,7 +220,7 @@ static void jffs2_erase_callback(struct erase_info *instr)
        struct erase_priv_struct *priv = (void *)instr->priv;
 
        if(instr->state != MTD_ERASE_DONE) {
-               printk(KERN_WARNING "Erase at 0x%08llx finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n",
+               pr_warn("Erase at 0x%08llx finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n",
                        (unsigned long long)instr->addr, instr->state);
                jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
        } else {
@@ -269,8 +275,8 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
                return;
        }
 
-       D1(printk(KERN_DEBUG "Removed nodes in range 0x%08x-0x%08x from ino #%u\n",
-                 jeb->offset, jeb->offset + c->sector_size, ic->ino));
+       jffs2_dbg(1, "Removed nodes in range 0x%08x-0x%08x from ino #%u\n",
+                 jeb->offset, jeb->offset + c->sector_size, ic->ino);
 
        D2({
                int i=0;
@@ -281,7 +287,7 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
 
                printk(KERN_DEBUG);
                while(this) {
-                       printk(KERN_CONT "0x%08x(%d)->",
+                       pr_cont("0x%08x(%d)->",
                               ref_offset(this), ref_flags(this));
                        if (++i == 5) {
                                printk(KERN_DEBUG);
@@ -289,7 +295,7 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
                        }
                        this = this->next_in_ino;
                }
-               printk(KERN_CONT "\n");
+               pr_cont("\n");
        });
 
        switch (ic->class) {
@@ -310,7 +316,8 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
 void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
        struct jffs2_raw_node_ref *block, *ref;
-       D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset));
+       jffs2_dbg(1, "Freeing all node refs for eraseblock offset 0x%08x\n",
+                 jeb->offset);
 
        block = ref = jeb->first_node;
 
@@ -342,12 +349,13 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl
                        &ebuf, NULL);
        if (ret != -EOPNOTSUPP) {
                if (ret) {
-                       D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
+                       jffs2_dbg(1, "MTD point failed %d\n", ret);
                        goto do_flash_read;
                }
                if (retlen < c->sector_size) {
                        /* Don't muck about if it won't let us point to the whole erase sector */
-                       D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
+                       jffs2_dbg(1, "MTD point returned len too short: 0x%zx\n",
+                                 retlen);
                        mtd_unpoint(c->mtd, jeb->offset, retlen);
                        goto do_flash_read;
                }
@@ -359,8 +367,10 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl
                } while(--retlen);
                mtd_unpoint(c->mtd, jeb->offset, c->sector_size);
                if (retlen) {
-                       printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08tx\n",
-                              *wordebuf, jeb->offset + c->sector_size-retlen*sizeof(*wordebuf));
+                       pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08tx\n",
+                               *wordebuf,
+                               jeb->offset +
+                               c->sector_size-retlen * sizeof(*wordebuf));
                        return -EIO;
                }
                return 0;
@@ -368,11 +378,12 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl
  do_flash_read:
        ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
        if (!ebuf) {
-               printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset);
+               pr_warn("Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n",
+                       jeb->offset);
                return -EAGAIN;
        }
 
-       D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
+       jffs2_dbg(1, "Verifying erase at 0x%08x\n", jeb->offset);
 
        for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) {
                uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
@@ -382,12 +393,14 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl
 
                ret = mtd_read(c->mtd, ofs, readlen, &retlen, ebuf);
                if (ret) {
-                       printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
+                       pr_warn("Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n",
+                               ofs, ret);
                        ret = -EIO;
                        goto fail;
                }
                if (retlen != readlen) {
-                       printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
+                       pr_warn("Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n",
+                               ofs, readlen, retlen);
                        ret = -EIO;
                        goto fail;
                }
@@ -396,7 +409,8 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl
                        unsigned long *datum = ebuf + i;
                        if (*datum + 1) {
                                *bad_offset += i;
-                               printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", *datum, *bad_offset);
+                               pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08x\n",
+                                       *datum, *bad_offset);
                                ret = -EIO;
                                goto fail;
                        }
@@ -422,7 +436,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
        }
 
        /* Write the erase complete marker */
-       D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+       jffs2_dbg(1, "Writing erased marker to block at 0x%08x\n", jeb->offset);
        bad_offset = jeb->offset;
 
        /* Cleanmarker in oob area or no cleanmarker at all ? */
@@ -451,10 +465,10 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
 
                if (ret || retlen != sizeof(marker)) {
                        if (ret)
-                               printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+                               pr_warn("Write clean marker to block at 0x%08x failed: %d\n",
                                       jeb->offset, ret);
                        else
-                               printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+                               pr_warn("Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
                                       jeb->offset, sizeof(marker), retlen);
 
                        goto filebad;
index 61e6723535b9d56f6cf727328ca7ae8d2a94090a..db3889ba8818dd473f37ba3b9e0b888e48eef708 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/time.h>
@@ -85,7 +87,8 @@ static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
        unsigned char *pg_buf;
        int ret;
 
-       D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
+       jffs2_dbg(2, "%s(): ino #%lu, page at offset 0x%lx\n",
+                 __func__, inode->i_ino, pg->index << PAGE_CACHE_SHIFT);
 
        BUG_ON(!PageLocked(pg));
 
@@ -105,7 +108,7 @@ static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
        flush_dcache_page(pg);
        kunmap(pg);
 
-       D2(printk(KERN_DEBUG "readpage finished\n"));
+       jffs2_dbg(2, "readpage finished\n");
        return ret;
 }
 
@@ -144,7 +147,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
                return -ENOMEM;
        *pagep = pg;
 
-       D1(printk(KERN_DEBUG "jffs2_write_begin()\n"));
+       jffs2_dbg(1, "%s()\n", __func__);
 
        if (pageofs > inode->i_size) {
                /* Make new hole frag from old EOF to new page */
@@ -153,8 +156,8 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
                struct jffs2_full_dnode *fn;
                uint32_t alloc_len;
 
-               D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
-                         (unsigned int)inode->i_size, pageofs));
+               jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
+                         (unsigned int)inode->i_size, pageofs);
 
                ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
                                          ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
@@ -198,7 +201,8 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
                        f->metadata = NULL;
                }
                if (ret) {
-                       D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in write_begin, returned %d\n", ret));
+                       jffs2_dbg(1, "Eep. add_full_dnode_to_inode() failed in write_begin, returned %d\n",
+                                 ret);
                        jffs2_mark_node_obsolete(c, fn->raw);
                        jffs2_free_full_dnode(fn);
                        jffs2_complete_reservation(c);
@@ -222,7 +226,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
                if (ret)
                        goto out_page;
        }
-       D1(printk(KERN_DEBUG "end write_begin(). pg->flags %lx\n", pg->flags));
+       jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags);
        return ret;
 
 out_page:
@@ -248,8 +252,9 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping,
        int ret = 0;
        uint32_t writtenlen = 0;
 
-       D1(printk(KERN_DEBUG "jffs2_write_end(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
-                 inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
+       jffs2_dbg(1, "%s(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
+                 __func__, inode->i_ino, pg->index << PAGE_CACHE_SHIFT,
+                 start, end, pg->flags);
 
        /* We need to avoid deadlock with page_cache_read() in
           jffs2_garbage_collect_pass(). So the page must be
@@ -268,7 +273,8 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping,
        ri = jffs2_alloc_raw_inode();
 
        if (!ri) {
-               D1(printk(KERN_DEBUG "jffs2_write_end(): Allocation of raw inode failed\n"));
+               jffs2_dbg(1, "%s(): Allocation of raw inode failed\n",
+                         __func__);
                unlock_page(pg);
                page_cache_release(pg);
                return -ENOMEM;
@@ -315,13 +321,14 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping,
                /* generic_file_write has written more to the page cache than we've
                   actually written to the medium. Mark the page !Uptodate so that
                   it gets reread */
-               D1(printk(KERN_DEBUG "jffs2_write_end(): Not all bytes written. Marking page !uptodate\n"));
+               jffs2_dbg(1, "%s(): Not all bytes written. Marking page !uptodate\n",
+                       __func__);
                SetPageError(pg);
                ClearPageUptodate(pg);
        }
 
-       D1(printk(KERN_DEBUG "jffs2_write_end() returning %d\n",
-                                       writtenlen > 0 ? writtenlen : ret));
+       jffs2_dbg(1, "%s() returning %d\n",
+                 __func__, writtenlen > 0 ? writtenlen : ret);
        unlock_page(pg);
        page_cache_release(pg);
        return writtenlen > 0 ? writtenlen : ret;
index c0d5c9d770dadc8df1afbaaeaa5136813aa746ac..bb6f993ebca924dd39471163b586ded206096f62 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/capability.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -39,7 +41,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
        int ret;
        int alloc_type = ALLOC_NORMAL;
 
-       D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
+       jffs2_dbg(1, "%s(): ino #%lu\n", __func__, inode->i_ino);
 
        /* Special cases - we don't want more than one data node
           for these types on the medium at any time. So setattr
@@ -50,7 +52,8 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
                /* For these, we don't actually need to read the old node */
                mdatalen = jffs2_encode_dev(&dev, inode->i_rdev);
                mdata = (char *)&dev;
-               D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
+               jffs2_dbg(1, "%s(): Writing %d bytes of kdev_t\n",
+                         __func__, mdatalen);
        } else if (S_ISLNK(inode->i_mode)) {
                mutex_lock(&f->sem);
                mdatalen = f->metadata->size;
@@ -66,7 +69,8 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
                        return ret;
                }
                mutex_unlock(&f->sem);
-               D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
+               jffs2_dbg(1, "%s(): Writing %d bytes of symlink target\n",
+                         __func__, mdatalen);
        }
 
        ri = jffs2_alloc_raw_inode();
@@ -233,7 +237,8 @@ void jffs2_evict_inode (struct inode *inode)
        struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
        struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 
-       D1(printk(KERN_DEBUG "jffs2_evict_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+       jffs2_dbg(1, "%s(): ino #%lu mode %o\n",
+                 __func__, inode->i_ino, inode->i_mode);
        truncate_inode_pages(&inode->i_data, 0);
        end_writeback(inode);
        jffs2_do_clear_inode(c, f);
@@ -249,7 +254,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
        dev_t rdev = 0;
        int ret;
 
-       D1(printk(KERN_DEBUG "jffs2_iget(): ino == %lu\n", ino));
+       jffs2_dbg(1, "%s(): ino == %lu\n", __func__, ino);
 
        inode = iget_locked(sb, ino);
        if (!inode)
@@ -317,14 +322,16 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
                /* Read the device numbers from the media */
                if (f->metadata->size != sizeof(jdev.old_id) &&
                    f->metadata->size != sizeof(jdev.new_id)) {
-                       printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size);
+                       pr_notice("Device node has strange size %d\n",
+                                 f->metadata->size);
                        goto error_io;
                }
-               D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
+               jffs2_dbg(1, "Reading device numbers from flash\n");
                ret = jffs2_read_dnode(c, f, f->metadata, (char *)&jdev, 0, f->metadata->size);
                if (ret < 0) {
                        /* Eep */
-                       printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
+                       pr_notice("Read device numbers for inode %lu failed\n",
+                                 (unsigned long)inode->i_ino);
                        goto error;
                }
                if (f->metadata->size == sizeof(jdev.old_id))
@@ -339,12 +346,13 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
                break;
 
        default:
-               printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
+               pr_warn("%s(): Bogus i_mode %o for ino %lu\n",
+                       __func__, inode->i_mode, (unsigned long)inode->i_ino);
        }
 
        mutex_unlock(&f->sem);
 
-       D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
+       jffs2_dbg(1, "jffs2_read_inode() returning\n");
        unlock_new_inode(inode);
        return inode;
 
@@ -362,11 +370,13 @@ void jffs2_dirty_inode(struct inode *inode, int flags)
        struct iattr iattr;
 
        if (!(inode->i_state & I_DIRTY_DATASYNC)) {
-               D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
+               jffs2_dbg(2, "%s(): not calling setattr() for ino #%lu\n",
+                         __func__, inode->i_ino);
                return;
        }
 
-       D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino));
+       jffs2_dbg(1, "%s(): calling setattr() for ino #%lu\n",
+                 __func__, inode->i_ino);
 
        iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME;
        iattr.ia_mode = inode->i_mode;
@@ -414,7 +424,8 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
        struct jffs2_inode_info *f;
        int ret;
 
-       D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
+       jffs2_dbg(1, "%s(): dir_i %ld, mode 0x%x\n",
+                 __func__, dir_i->i_ino, mode);
 
        c = JFFS2_SB_INFO(sb);
 
@@ -504,11 +515,11 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
 
 #ifndef CONFIG_JFFS2_FS_WRITEBUFFER
        if (c->mtd->type == MTD_NANDFLASH) {
-               printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
+               pr_err("Cannot operate on NAND flash unless jffs2 NAND support is compiled in\n");
                return -EINVAL;
        }
        if (c->mtd->type == MTD_DATAFLASH) {
-               printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
+               pr_err("Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in\n");
                return -EINVAL;
        }
 #endif
@@ -522,12 +533,13 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
         */
        if ((c->sector_size * blocks) != c->flash_size) {
                c->flash_size = c->sector_size * blocks;
-               printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
+               pr_info("Flash size not aligned to erasesize, reducing to %dKiB\n",
                        c->flash_size / 1024);
        }
 
        if (c->flash_size < 5*c->sector_size) {
-               printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size);
+               pr_err("Too few erase blocks (%d)\n",
+                      c->flash_size / c->sector_size);
                return -EINVAL;
        }
 
@@ -550,17 +562,17 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
        if ((ret = jffs2_do_mount_fs(c)))
                goto out_inohash;
 
-       D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n"));
+       jffs2_dbg(1, "%s(): Getting root inode\n", __func__);
        root_i = jffs2_iget(sb, 1);
        if (IS_ERR(root_i)) {
-               D1(printk(KERN_WARNING "get root inode failed\n"));
+               jffs2_dbg(1, "get root inode failed\n");
                ret = PTR_ERR(root_i);
                goto out_root;
        }
 
        ret = -ENOMEM;
 
-       D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n"));
+       jffs2_dbg(1, "%s(): d_make_root()\n", __func__);
        sb->s_root = d_make_root(root_i);
        if (!sb->s_root)
                goto out_root;
@@ -618,20 +630,21 @@ struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
                */
                inode = ilookup(OFNI_BS_2SFFJ(c), inum);
                if (!inode) {
-                       D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
-                                 inum));
+                       jffs2_dbg(1, "ilookup() failed for ino #%u; inode is probably deleted.\n",
+                                 inum);
 
                        spin_lock(&c->inocache_lock);
                        ic = jffs2_get_ino_cache(c, inum);
                        if (!ic) {
-                               D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
+                               jffs2_dbg(1, "Inode cache for ino #%u is gone\n",
+                                         inum);
                                spin_unlock(&c->inocache_lock);
                                return NULL;
                        }
                        if (ic->state != INO_STATE_CHECKEDABSENT) {
                                /* Wait for progress. Don't just loop */
-                               D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
-                                         ic->ino, ic->state));
+                               jffs2_dbg(1, "Waiting for ino #%u in state %d\n",
+                                         ic->ino, ic->state);
                                sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
                        } else {
                                spin_unlock(&c->inocache_lock);
@@ -649,8 +662,8 @@ struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
                        return ERR_CAST(inode);
        }
        if (is_bad_inode(inode)) {
-               printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. unlinked %d\n",
-                      inum, unlinked);
+               pr_notice("Eep. read_inode() failed for ino #%u. unlinked %d\n",
+                         inum, unlinked);
                /* NB. This will happen again. We need to do something appropriate here. */
                iput(inode);
                return ERR_PTR(-EIO);
index 31dce611337cffcf67e8330bb2f6b6e8608bde1e..ad271c70aa252feac98c4b6f25391ad27a66920b 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/mtd/mtd.h>
 #include <linux/slab.h>
@@ -51,44 +53,44 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
           number of free blocks is low. */
 again:
        if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
-               D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
+               jffs2_dbg(1, "Picking block from bad_used_list to GC next\n");
                nextlist = &c->bad_used_list;
        } else if (n < 50 && !list_empty(&c->erasable_list)) {
                /* Note that most of them will have gone directly to be erased.
                   So don't favour the erasable_list _too_ much. */
-               D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n"));
+               jffs2_dbg(1, "Picking block from erasable_list to GC next\n");
                nextlist = &c->erasable_list;
        } else if (n < 110 && !list_empty(&c->very_dirty_list)) {
                /* Most of the time, pick one off the very_dirty list */
-               D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n"));
+               jffs2_dbg(1, "Picking block from very_dirty_list to GC next\n");
                nextlist = &c->very_dirty_list;
        } else if (n < 126 && !list_empty(&c->dirty_list)) {
-               D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n"));
+               jffs2_dbg(1, "Picking block from dirty_list to GC next\n");
                nextlist = &c->dirty_list;
        } else if (!list_empty(&c->clean_list)) {
-               D1(printk(KERN_DEBUG "Picking block from clean_list to GC next\n"));
+               jffs2_dbg(1, "Picking block from clean_list to GC next\n");
                nextlist = &c->clean_list;
        } else if (!list_empty(&c->dirty_list)) {
-               D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
+               jffs2_dbg(1, "Picking block from dirty_list to GC next (clean_list was empty)\n");
 
                nextlist = &c->dirty_list;
        } else if (!list_empty(&c->very_dirty_list)) {
-               D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n"));
+               jffs2_dbg(1, "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n");
                nextlist = &c->very_dirty_list;
        } else if (!list_empty(&c->erasable_list)) {
-               D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
+               jffs2_dbg(1, "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n");
 
                nextlist = &c->erasable_list;
        } else if (!list_empty(&c->erasable_pending_wbuf_list)) {
                /* There are blocks are wating for the wbuf sync */
-               D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n"));
+               jffs2_dbg(1, "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n");
                spin_unlock(&c->erase_completion_lock);
                jffs2_flush_wbuf_pad(c);
                spin_lock(&c->erase_completion_lock);
                goto again;
        } else {
                /* Eep. All were empty */
-               D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
+               jffs2_dbg(1, "No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
                return NULL;
        }
 
@@ -97,13 +99,15 @@ again:
        c->gcblock = ret;
        ret->gc_node = ret->first_node;
        if (!ret->gc_node) {
-               printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset);
+               pr_warn("Eep. ret->gc_node for block at 0x%08x is NULL\n",
+                       ret->offset);
                BUG();
        }
 
        /* Have we accidentally picked a clean block with wasted space ? */
        if (ret->wasted_size) {
-               D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size));
+               jffs2_dbg(1, "Converting wasted_size %08x to dirty_size\n",
+                         ret->wasted_size);
                ret->dirty_size += ret->wasted_size;
                c->wasted_size -= ret->wasted_size;
                c->dirty_size += ret->wasted_size;
@@ -140,8 +144,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 
                /* checked_ino is protected by the alloc_sem */
                if (c->checked_ino > c->highest_ino && xattr) {
-                       printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
-                              c->unchecked_size);
+                       pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n",
+                               c->unchecked_size);
                        jffs2_dbg_dump_block_lists_nolock(c);
                        spin_unlock(&c->erase_completion_lock);
                        mutex_unlock(&c->alloc_sem);
@@ -163,8 +167,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                }
 
                if (!ic->pino_nlink) {
-                       D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink/pino zero\n",
-                                 ic->ino));
+                       jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n",
+                                 ic->ino);
                        spin_unlock(&c->inocache_lock);
                        jffs2_xattr_delete_inode(c, ic);
                        continue;
@@ -172,13 +176,15 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                switch(ic->state) {
                case INO_STATE_CHECKEDABSENT:
                case INO_STATE_PRESENT:
-                       D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino));
+                       jffs2_dbg(1, "Skipping ino #%u already checked\n",
+                                 ic->ino);
                        spin_unlock(&c->inocache_lock);
                        continue;
 
                case INO_STATE_GC:
                case INO_STATE_CHECKING:
-                       printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state);
+                       pr_warn("Inode #%u is in state %d during CRC check phase!\n",
+                               ic->ino, ic->state);
                        spin_unlock(&c->inocache_lock);
                        BUG();
 
@@ -186,7 +192,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                        /* We need to wait for it to finish, lest we move on
                           and trigger the BUG() above while we haven't yet
                           finished checking all its nodes */
-                       D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
+                       jffs2_dbg(1, "Waiting for ino #%u to finish reading\n",
+                                 ic->ino);
                        /* We need to come back again for the _same_ inode. We've
                         made no progress in this case, but that should be OK */
                        c->checked_ino--;
@@ -204,11 +211,13 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                ic->state = INO_STATE_CHECKING;
                spin_unlock(&c->inocache_lock);
 
-               D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino));
+               jffs2_dbg(1, "%s(): triggering inode scan of ino#%u\n",
+                         __func__, ic->ino);
 
                ret = jffs2_do_crccheck_inode(c, ic);
                if (ret)
-                       printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino);
+                       pr_warn("Returned error for crccheck of ino #%u. Expect badness...\n",
+                               ic->ino);
 
                jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
                mutex_unlock(&c->alloc_sem);
@@ -220,11 +229,11 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
            !list_empty(&c->erase_pending_list)) {
                spin_unlock(&c->erase_completion_lock);
                mutex_unlock(&c->alloc_sem);
-               D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() erasing pending blocks\n"));
+               jffs2_dbg(1, "%s(): erasing pending blocks\n", __func__);
                if (jffs2_erase_pending_blocks(c, 1))
                        return 0;
 
-               D1(printk(KERN_DEBUG "No progress from erasing blocks; doing GC anyway\n"));
+               jffs2_dbg(1, "No progress from erasing block; doing GC anyway\n");
                spin_lock(&c->erase_completion_lock);
                mutex_lock(&c->alloc_sem);
        }
@@ -242,13 +251,14 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                        mutex_unlock(&c->alloc_sem);
                        return -EAGAIN;
                }
-               D1(printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
+               jffs2_dbg(1, "Couldn't find erase block to garbage collect!\n");
                spin_unlock(&c->erase_completion_lock);
                mutex_unlock(&c->alloc_sem);
                return -EIO;
        }
 
-       D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size));
+       jffs2_dbg(1, "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n",
+                 jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size);
        D1(if (c->nextblock)
           printk(KERN_DEBUG "Nextblock at  %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size));
 
@@ -261,12 +271,14 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
        gcblock_dirty = jeb->dirty_size;
 
        while(ref_obsolete(raw)) {
-               D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
+               jffs2_dbg(1, "Node at 0x%08x is obsolete... skipping\n",
+                         ref_offset(raw));
                raw = ref_next(raw);
                if (unlikely(!raw)) {
-                       printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
-                       printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
-                              jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
+                       pr_warn("eep. End of raw list while still supposedly nodes to GC\n");
+                       pr_warn("erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
+                               jeb->offset, jeb->free_size,
+                               jeb->dirty_size, jeb->used_size);
                        jeb->gc_node = raw;
                        spin_unlock(&c->erase_completion_lock);
                        mutex_unlock(&c->alloc_sem);
@@ -275,7 +287,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
        }
        jeb->gc_node = raw;
 
-       D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
+       jffs2_dbg(1, "Going to garbage collect node at 0x%08x\n",
+                 ref_offset(raw));
 
        if (!raw->next_in_ino) {
                /* Inode-less node. Clean marker, snapshot or something like that */
@@ -316,7 +329,9 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 
        spin_unlock(&c->erase_completion_lock);
 
-       D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino));
+       jffs2_dbg(1, "%s(): collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n",
+                 __func__, jeb->offset, ref_offset(raw), ref_flags(raw),
+                 ic->ino);
 
        /* Three possibilities:
           1. Inode is already in-core. We must iget it and do proper
@@ -336,8 +351,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                if (ref_flags(raw) == REF_PRISTINE)
                        ic->state = INO_STATE_GC;
                else {
-                       D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
-                                 ic->ino));
+                       jffs2_dbg(1, "Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
+                                 ic->ino);
                }
                break;
 
@@ -353,8 +368,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                   we're holding the alloc_sem, no other garbage collection
                   can happen.
                */
-               printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
-                      ic->ino, ic->state);
+               pr_crit("Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
+                       ic->ino, ic->state);
                mutex_unlock(&c->alloc_sem);
                spin_unlock(&c->inocache_lock);
                BUG();
@@ -367,8 +382,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                   drop the alloc_sem before sleeping. */
 
                mutex_unlock(&c->alloc_sem);
-               D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
-                         ic->ino, ic->state));
+               jffs2_dbg(1, "%s(): waiting for ino #%u in state %d\n",
+                         __func__, ic->ino, ic->state);
                sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
                /* And because we dropped the alloc_sem we must start again from the
                   beginning. Ponder chance of livelock here -- we're returning success
@@ -433,7 +448,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
  test_gcnode:
        if (jeb->dirty_size == gcblock_dirty && !ref_obsolete(jeb->gc_node)) {
                /* Eep. This really should never happen. GC is broken */
-               printk(KERN_ERR "Error garbage collecting node at %08x!\n", ref_offset(jeb->gc_node));
+               pr_err("Error garbage collecting node at %08x!\n",
+                      ref_offset(jeb->gc_node));
                ret = -ENOSPC;
        }
  release_sem:
@@ -445,7 +461,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 
  eraseit:
        if (c->gcblock && !c->gcblock->used_size) {
-               D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
+               jffs2_dbg(1, "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n",
+                         c->gcblock->offset);
                /* We're GC'ing an empty block? */
                list_add_tail(&c->gcblock->list, &c->erase_pending_list);
                c->gcblock = NULL;
@@ -475,12 +492,12 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_era
 
        if (c->gcblock != jeb) {
                spin_unlock(&c->erase_completion_lock);
-               D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n"));
+               jffs2_dbg(1, "GC block is no longer gcblock. Restart\n");
                goto upnout;
        }
        if (ref_obsolete(raw)) {
                spin_unlock(&c->erase_completion_lock);
-               D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
+               jffs2_dbg(1, "node to be GC'd was obsoleted in the meantime.\n");
                /* They'll call again */
                goto upnout;
        }
@@ -536,10 +553,10 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_era
        } else if (fd) {
                ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd);
        } else {
-               printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n",
-                      ref_offset(raw), f->inocache->ino);
+               pr_warn("Raw node at 0x%08x wasn't in node lists for ino #%u\n",
+                       ref_offset(raw), f->inocache->ino);
                if (ref_obsolete(raw)) {
-                       printk(KERN_WARNING "But it's obsolete so we don't mind too much\n");
+                       pr_warn("But it's obsolete so we don't mind too much\n");
                } else {
                        jffs2_dbg_dump_node(c, ref_offset(raw));
                        BUG();
@@ -562,7 +579,8 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
        uint32_t crc, rawlen;
        int retried = 0;
 
-       D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
+       jffs2_dbg(1, "Going to GC REF_PRISTINE node at 0x%08x\n",
+                 ref_offset(raw));
 
        alloclen = rawlen = ref_totlen(c, c->gcblock, raw);
 
@@ -595,8 +613,8 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 
        crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4);
        if (je32_to_cpu(node->u.hdr_crc) != crc) {
-               printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                      ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
+               pr_warn("Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+                       ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
                goto bail;
        }
 
@@ -604,16 +622,18 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
        case JFFS2_NODETYPE_INODE:
                crc = crc32(0, node, sizeof(node->i)-8);
                if (je32_to_cpu(node->i.node_crc) != crc) {
-                       printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                              ref_offset(raw), je32_to_cpu(node->i.node_crc), crc);
+                       pr_warn("Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+                               ref_offset(raw), je32_to_cpu(node->i.node_crc),
+                               crc);
                        goto bail;
                }
 
                if (je32_to_cpu(node->i.dsize)) {
                        crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize));
                        if (je32_to_cpu(node->i.data_crc) != crc) {
-                               printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                                      ref_offset(raw), je32_to_cpu(node->i.data_crc), crc);
+                               pr_warn("Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+                                       ref_offset(raw),
+                                       je32_to_cpu(node->i.data_crc), crc);
                                goto bail;
                        }
                }
@@ -622,21 +642,24 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
        case JFFS2_NODETYPE_DIRENT:
                crc = crc32(0, node, sizeof(node->d)-8);
                if (je32_to_cpu(node->d.node_crc) != crc) {
-                       printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                              ref_offset(raw), je32_to_cpu(node->d.node_crc), crc);
+                       pr_warn("Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+                               ref_offset(raw),
+                               je32_to_cpu(node->d.node_crc), crc);
                        goto bail;
                }
 
                if (strnlen(node->d.name, node->d.nsize) != node->d.nsize) {
-                       printk(KERN_WARNING "Name in dirent node at 0x%08x contains zeroes\n", ref_offset(raw));
+                       pr_warn("Name in dirent node at 0x%08x contains zeroes\n",
+                               ref_offset(raw));
                        goto bail;
                }
 
                if (node->d.nsize) {
                        crc = crc32(0, node->d.name, node->d.nsize);
                        if (je32_to_cpu(node->d.name_crc) != crc) {
-                               printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                                      ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
+                               pr_warn("Name CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+                                       ref_offset(raw),
+                                       je32_to_cpu(node->d.name_crc), crc);
                                goto bail;
                        }
                }
@@ -644,8 +667,8 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
        default:
                /* If it's inode-less, we don't _know_ what it is. Just copy it intact */
                if (ic) {
-                       printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
-                              ref_offset(raw), je16_to_cpu(node->u.nodetype));
+                       pr_warn("Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
+                               ref_offset(raw), je16_to_cpu(node->u.nodetype));
                        goto bail;
                }
        }
@@ -657,12 +680,13 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
        ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
 
        if (ret || (retlen != rawlen)) {
-               printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
-                      rawlen, phys_ofs, ret, retlen);
+               pr_notice("Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
+                         rawlen, phys_ofs, ret, retlen);
                if (retlen) {
                        jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL);
                } else {
-                       printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", phys_ofs);
+                       pr_notice("Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n",
+                                 phys_ofs);
                }
                if (!retried) {
                        /* Try to reallocate space and retry */
@@ -671,7 +695,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 
                        retried = 1;
 
-                       D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n"));
+                       jffs2_dbg(1, "Retrying failed write of REF_PRISTINE node.\n");
 
                        jffs2_dbg_acct_sanity_check(c,jeb);
                        jffs2_dbg_acct_paranoia_check(c, jeb);
@@ -681,14 +705,16 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
                                                        it is only an upper estimation */
 
                        if (!ret) {
-                               D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
+                               jffs2_dbg(1, "Allocated space at 0x%08x to retry failed write.\n",
+                                         phys_ofs);
 
                                jffs2_dbg_acct_sanity_check(c,jeb);
                                jffs2_dbg_acct_paranoia_check(c, jeb);
 
                                goto retry;
                        }
-                       D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+                       jffs2_dbg(1, "Failed to allocate space to retry failed write: %d!\n",
+                                 ret);
                }
 
                if (!ret)
@@ -698,7 +724,8 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
        jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, rawlen, ic);
 
        jffs2_mark_node_obsolete(c, raw);
-       D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
+       jffs2_dbg(1, "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n",
+                 ref_offset(raw));
 
  out_node:
        kfree(node);
@@ -725,29 +752,32 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
                /* For these, we don't actually need to read the old node */
                mdatalen = jffs2_encode_dev(&dev, JFFS2_F_I_RDEV(f));
                mdata = (char *)&dev;
-               D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
+               jffs2_dbg(1, "%s(): Writing %d bytes of kdev_t\n",
+                         __func__, mdatalen);
        } else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
                mdatalen = fn->size;
                mdata = kmalloc(fn->size, GFP_KERNEL);
                if (!mdata) {
-                       printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
+                       pr_warn("kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
                        return -ENOMEM;
                }
                ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
                if (ret) {
-                       printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
+                       pr_warn("read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n",
+                               ret);
                        kfree(mdata);
                        return ret;
                }
-               D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bites of symlink target\n", mdatalen));
+               jffs2_dbg(1, "%s(): Writing %d bites of symlink target\n",
+                         __func__, mdatalen);
 
        }
 
        ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &alloclen,
                                JFFS2_SUMMARY_INODE_SIZE);
        if (ret) {
-               printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
-                      sizeof(ri)+ mdatalen, ret);
+               pr_warn("jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
+                       sizeof(ri) + mdatalen, ret);
                goto out;
        }
 
@@ -784,7 +814,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
        new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, ALLOC_GC);
 
        if (IS_ERR(new_fn)) {
-               printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+               pr_warn("Error writing new dnode: %ld\n", PTR_ERR(new_fn));
                ret = PTR_ERR(new_fn);
                goto out;
        }
@@ -827,14 +857,15 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
        ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &alloclen,
                                JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize));
        if (ret) {
-               printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
-                      sizeof(rd)+rd.nsize, ret);
+               pr_warn("jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
+                       sizeof(rd)+rd.nsize, ret);
                return ret;
        }
        new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, ALLOC_GC);
 
        if (IS_ERR(new_fd)) {
-               printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
+               pr_warn("jffs2_write_dirent in garbage_collect_dirent failed: %ld\n",
+                       PTR_ERR(new_fd));
                return PTR_ERR(new_fd);
        }
        jffs2_add_fd_to_list(c, new_fd, &f->dents);
@@ -887,19 +918,22 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
                        if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
                                continue;
 
-                       D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
+                       jffs2_dbg(1, "Check potential deletion dirent at %08x\n",
+                                 ref_offset(raw));
 
                        /* This is an obsolete node belonging to the same directory, and it's of the right
                           length. We need to take a closer look...*/
                        ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd);
                        if (ret) {
-                               printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw));
+                               pr_warn("%s(): Read error (%d) reading obsolete node at %08x\n",
+                                       __func__, ret, ref_offset(raw));
                                /* If we can't read it, we don't need to continue to obsolete it. Continue */
                                continue;
                        }
                        if (retlen != rawlen) {
-                               printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %u) reading header from obsolete node at %08x\n",
-                                      retlen, rawlen, ref_offset(raw));
+                               pr_warn("%s(): Short read (%zd not %u) reading header from obsolete node at %08x\n",
+                                       __func__, retlen, rawlen,
+                                       ref_offset(raw));
                                continue;
                        }
 
@@ -923,8 +957,9 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
                           a new deletion dirent to replace it */
                        mutex_unlock(&c->erase_free_sem);
 
-                       D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
-                                 ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
+                       jffs2_dbg(1, "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
+                                 ref_offset(fd->raw), fd->name,
+                                 ref_offset(raw), je32_to_cpu(rd->ino));
                        kfree(rd);
 
                        return jffs2_garbage_collect_dirent(c, jeb, f, fd);
@@ -947,7 +982,8 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
                fdp = &(*fdp)->next;
        }
        if (!found) {
-               printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino);
+               pr_warn("Deletion dirent \"%s\" not found in list for ino #%u\n",
+                       fd->name, f->inocache->ino);
        }
        jffs2_mark_node_obsolete(c, fd->raw);
        jffs2_free_full_dirent(fd);
@@ -964,8 +1000,8 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
        uint32_t alloclen, ilen;
        int ret;
 
-       D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
-                 f->inocache->ino, start, end));
+       jffs2_dbg(1, "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
+                 f->inocache->ino, start, end);
 
        memset(&ri, 0, sizeof(ri));
 
@@ -976,35 +1012,37 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
                   write it out again with the _same_ version as before */
                ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
                if (readlen != sizeof(ri) || ret) {
-                       printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen);
+                       pr_warn("Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n",
+                               ret, readlen);
                        goto fill;
                }
                if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
-                       printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
-                              ref_offset(fn->raw),
-                              je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
+                       pr_warn("%s(): Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
+                               __func__, ref_offset(fn->raw),
+                               je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
                        return -EIO;
                }
                if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
-                       printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
-                              ref_offset(fn->raw),
-                              je32_to_cpu(ri.totlen), sizeof(ri));
+                       pr_warn("%s(): Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
+                               __func__, ref_offset(fn->raw),
+                               je32_to_cpu(ri.totlen), sizeof(ri));
                        return -EIO;
                }
                crc = crc32(0, &ri, sizeof(ri)-8);
                if (crc != je32_to_cpu(ri.node_crc)) {
-                       printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
-                              ref_offset(fn->raw),
-                              je32_to_cpu(ri.node_crc), crc);
+                       pr_warn("%s: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
+                               __func__, ref_offset(fn->raw),
+                               je32_to_cpu(ri.node_crc), crc);
                        /* FIXME: We could possibly deal with this by writing new holes for each frag */
-                       printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
-                              start, end, f->inocache->ino);
+                       pr_warn("Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
+                               start, end, f->inocache->ino);
                        goto fill;
                }
                if (ri.compr != JFFS2_COMPR_ZERO) {
-                       printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw));
-                       printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
-                              start, end, f->inocache->ino);
+                       pr_warn("%s(): Node 0x%08x wasn't a hole node!\n",
+                               __func__, ref_offset(fn->raw));
+                       pr_warn("Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
+                               start, end, f->inocache->ino);
                        goto fill;
                }
        } else {
@@ -1043,14 +1081,14 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
        ret = jffs2_reserve_space_gc(c, sizeof(ri), &alloclen,
                                     JFFS2_SUMMARY_INODE_SIZE);
        if (ret) {
-               printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
-                      sizeof(ri), ret);
+               pr_warn("jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
+                       sizeof(ri), ret);
                return ret;
        }
        new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_GC);
 
        if (IS_ERR(new_fn)) {
-               printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
+               pr_warn("Error writing new hole node: %ld\n", PTR_ERR(new_fn));
                return PTR_ERR(new_fn);
        }
        if (je32_to_cpu(ri.version) == f->highest_version) {
@@ -1070,9 +1108,9 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
         * above.)
         */
        D1(if(unlikely(fn->frags <= 1)) {
-               printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
-                      fn->frags, je32_to_cpu(ri.version), f->highest_version,
-                      je32_to_cpu(ri.ino));
+                       pr_warn("%s(): Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
+                               __func__, fn->frags, je32_to_cpu(ri.version),
+                               f->highest_version, je32_to_cpu(ri.ino));
        });
 
        /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
@@ -1089,11 +1127,11 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
                }
        }
        if (fn->frags) {
-               printk(KERN_WARNING "jffs2_garbage_collect_hole: Old node still has frags!\n");
+               pr_warn("%s(): Old node still has frags!\n", __func__);
                BUG();
        }
        if (!new_fn->frags) {
-               printk(KERN_WARNING "jffs2_garbage_collect_hole: New node has no frags!\n");
+               pr_warn("%s(): New node has no frags!\n", __func__);
                BUG();
        }
 
@@ -1117,8 +1155,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
 
        memset(&ri, 0, sizeof(ri));
 
-       D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
-                 f->inocache->ino, start, end));
+       jffs2_dbg(1, "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
+                 f->inocache->ino, start, end);
 
        orig_end = end;
        orig_start = start;
@@ -1149,15 +1187,15 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
                        /* If the previous frag doesn't even reach the beginning, there's
                           excessive fragmentation. Just merge. */
                        if (frag->ofs > min) {
-                               D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n",
-                                         frag->ofs, frag->ofs+frag->size));
+                               jffs2_dbg(1, "Expanding down to cover partial frag (0x%x-0x%x)\n",
+                                         frag->ofs, frag->ofs+frag->size);
                                start = frag->ofs;
                                continue;
                        }
                        /* OK. This frag holds the first byte of the page. */
                        if (!frag->node || !frag->node->raw) {
-                               D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
-                                         frag->ofs, frag->ofs+frag->size));
+                               jffs2_dbg(1, "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
+                                         frag->ofs, frag->ofs+frag->size);
                                break;
                        } else {
 
@@ -1171,19 +1209,25 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
                                jeb = &c->blocks[raw->flash_offset / c->sector_size];
 
                                if (jeb == c->gcblock) {
-                                       D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
-                                                 frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
+                                       jffs2_dbg(1, "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
+                                                 frag->ofs,
+                                                 frag->ofs + frag->size,
+                                                 ref_offset(raw));
                                        start = frag->ofs;
                                        break;
                                }
                                if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
-                                       D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
-                                                 frag->ofs, frag->ofs+frag->size, jeb->offset));
+                                       jffs2_dbg(1, "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
+                                                 frag->ofs,
+                                                 frag->ofs + frag->size,
+                                                 jeb->offset);
                                        break;
                                }
 
-                               D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
-                                                 frag->ofs, frag->ofs+frag->size, jeb->offset));
+                               jffs2_dbg(1, "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
+                                         frag->ofs,
+                                         frag->ofs + frag->size,
+                                         jeb->offset);
                                start = frag->ofs;
                                break;
                        }
@@ -1199,15 +1243,15 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
                        /* If the previous frag doesn't even reach the beginning, there's lots
                           of fragmentation. Just merge. */
                        if (frag->ofs+frag->size < max) {
-                               D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n",
-                                         frag->ofs, frag->ofs+frag->size));
+                               jffs2_dbg(1, "Expanding up to cover partial frag (0x%x-0x%x)\n",
+                                         frag->ofs, frag->ofs+frag->size);
                                end = frag->ofs + frag->size;
                                continue;
                        }
 
                        if (!frag->node || !frag->node->raw) {
-                               D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
-                                         frag->ofs, frag->ofs+frag->size));
+                               jffs2_dbg(1, "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
+                                         frag->ofs, frag->ofs+frag->size);
                                break;
                        } else {
 
@@ -1221,25 +1265,31 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
                                jeb = &c->blocks[raw->flash_offset / c->sector_size];
 
                                if (jeb == c->gcblock) {
-                                       D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
-                                                 frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
+                                       jffs2_dbg(1, "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
+                                                 frag->ofs,
+                                                 frag->ofs + frag->size,
+                                                 ref_offset(raw));
                                        end = frag->ofs + frag->size;
                                        break;
                                }
                                if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
-                                       D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
-                                                 frag->ofs, frag->ofs+frag->size, jeb->offset));
+                                       jffs2_dbg(1, "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
+                                                 frag->ofs,
+                                                 frag->ofs + frag->size,
+                                                 jeb->offset);
                                        break;
                                }
 
-                               D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
-                                                 frag->ofs, frag->ofs+frag->size, jeb->offset));
+                               jffs2_dbg(1, "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
+                                         frag->ofs,
+                                         frag->ofs + frag->size,
+                                         jeb->offset);
                                end = frag->ofs + frag->size;
                                break;
                        }
                }
-               D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
-                         orig_start, orig_end, start, end));
+               jffs2_dbg(1, "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
+                         orig_start, orig_end, start, end);
 
                D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size));
                BUG_ON(end < orig_end);
@@ -1256,7 +1306,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
        pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
 
        if (IS_ERR(pg_ptr)) {
-               printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr));
+               pr_warn("read_cache_page() returned error: %ld\n",
+                       PTR_ERR(pg_ptr));
                return PTR_ERR(pg_ptr);
        }
 
@@ -1270,8 +1321,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
                                        &alloclen, JFFS2_SUMMARY_INODE_SIZE);
 
                if (ret) {
-                       printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
-                              sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
+                       pr_warn("jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
+                               sizeof(ri) + JFFS2_MIN_DATA_LEN, ret);
                        break;
                }
                cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
@@ -1308,7 +1359,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
                jffs2_free_comprbuf(comprbuf, writebuf);
 
                if (IS_ERR(new_fn)) {
-                       printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+                       pr_warn("Error writing new dnode: %ld\n",
+                               PTR_ERR(new_fn));
                        ret = PTR_ERR(new_fn);
                        break;
                }
index c082868910f2b54482fcd20340c493e804ea375e..4f47aa24b5562001cf8983d6c7634c373d50206b 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/init.h>
index 5e03233c2363e2476998a473edb989f21c50f752..975a1f562c10de31f3859b1dded82d134f4c2d0e 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
@@ -687,8 +689,8 @@ int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
        if (!size)
                return 0;
        if (unlikely(size > jeb->free_size)) {
-               printk(KERN_CRIT "Dirty space 0x%x larger then free_size 0x%x (wasted 0x%x)\n",
-                      size, jeb->free_size, jeb->wasted_size);
+               pr_crit("Dirty space 0x%x larger then free_size 0x%x (wasted 0x%x)\n",
+                       size, jeb->free_size, jeb->wasted_size);
                BUG();
        }
        /* REF_EMPTY_NODE is !obsolete, so that works OK */
@@ -726,8 +728,10 @@ static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
 
                /* Last node in block. Use free_space */
                if (unlikely(ref != jeb->last_node)) {
-                       printk(KERN_CRIT "ref %p @0x%08x is not jeb->last_node (%p @0x%08x)\n",
-                              ref, ref_offset(ref), jeb->last_node, jeb->last_node?ref_offset(jeb->last_node):0);
+                       pr_crit("ref %p @0x%08x is not jeb->last_node (%p @0x%08x)\n",
+                               ref, ref_offset(ref), jeb->last_node,
+                               jeb->last_node ?
+                               ref_offset(jeb->last_node) : 0);
                        BUG();
                }
                ref_end = jeb->offset + c->sector_size - jeb->free_size;
@@ -747,16 +751,20 @@ uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
                if (!jeb)
                        jeb = &c->blocks[ref->flash_offset / c->sector_size];
 
-               printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
-                      ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
-                      ret, ref->__totlen);
+               pr_crit("Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
+                       ref, ref_offset(ref), ref_offset(ref) + ref->__totlen,
+                       ret, ref->__totlen);
                if (ref_next(ref)) {
-                       printk(KERN_CRIT "next %p (0x%08x-0x%08x)\n", ref_next(ref), ref_offset(ref_next(ref)),
-                              ref_offset(ref_next(ref))+ref->__totlen);
+                       pr_crit("next %p (0x%08x-0x%08x)\n",
+                               ref_next(ref), ref_offset(ref_next(ref)),
+                               ref_offset(ref_next(ref)) + ref->__totlen);
                } else 
-                       printk(KERN_CRIT "No next ref. jeb->last_node is %p\n", jeb->last_node);
+                       pr_crit("No next ref. jeb->last_node is %p\n",
+                               jeb->last_node);
 
-               printk(KERN_CRIT "jeb->wasted_size %x, dirty_size %x, used_size %x, free_size %x\n", jeb->wasted_size, jeb->dirty_size, jeb->used_size, jeb->free_size);
+               pr_crit("jeb->wasted_size %x, dirty_size %x, used_size %x, free_size %x\n",
+                       jeb->wasted_size, jeb->dirty_size, jeb->used_size,
+                       jeb->free_size);
 
 #if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS)
                __jffs2_dbg_dump_node_refs_nolock(c, jeb);
index 694aa5b035057d5fa17ab8a475dfd41d0c9e681d..6784d1e7a7eb3440b7e7707a4659f79e8cec7433 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/mtd/mtd.h>
 #include <linux/compiler.h>
@@ -46,10 +48,10 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
        /* align it */
        minsize = PAD(minsize);
 
-       D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
+       jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize);
        mutex_lock(&c->alloc_sem);
 
-       D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
+       jffs2_dbg(1, "%s(): alloc sem got\n", __func__);
 
        spin_lock(&c->erase_completion_lock);
 
@@ -73,11 +75,13 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
                        dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
                        if (dirty < c->nospc_dirty_size) {
                                if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
-                                       D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n"));
+                                       jffs2_dbg(1, "%s(): Low on dirty space to GC, but it's a deletion. Allowing...\n",
+                                                 __func__);
                                        break;
                                }
-                               D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
-                                         dirty, c->unchecked_size, c->sector_size));
+                               jffs2_dbg(1, "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
+                                         dirty, c->unchecked_size,
+                                         c->sector_size);
 
                                spin_unlock(&c->erase_completion_lock);
                                mutex_unlock(&c->alloc_sem);
@@ -96,12 +100,13 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
                        avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
                        if ( (avail / c->sector_size) <= blocksneeded) {
                                if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
-                                       D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n"));
+                                       jffs2_dbg(1, "%s(): Low on possibly available space, but it's a deletion. Allowing...\n",
+                                                 __func__);
                                        break;
                                }
 
-                               D1(printk(KERN_DEBUG "max. available size 0x%08x  < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
-                                         avail, blocksneeded * c->sector_size));
+                               jffs2_dbg(1, "max. available size 0x%08x  < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
+                                         avail, blocksneeded * c->sector_size);
                                spin_unlock(&c->erase_completion_lock);
                                mutex_unlock(&c->alloc_sem);
                                return -ENOSPC;
@@ -109,9 +114,14 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
 
                        mutex_unlock(&c->alloc_sem);
 
-                       D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
-                                 c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
-                                 c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
+                       jffs2_dbg(1, "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
+                                 c->nr_free_blocks, c->nr_erasing_blocks,
+                                 c->free_size, c->dirty_size, c->wasted_size,
+                                 c->used_size, c->erasing_size, c->bad_size,
+                                 c->free_size + c->dirty_size +
+                                 c->wasted_size + c->used_size +
+                                 c->erasing_size + c->bad_size,
+                                 c->flash_size);
                        spin_unlock(&c->erase_completion_lock);
 
                        ret = jffs2_garbage_collect_pass(c);
@@ -124,7 +134,8 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
                                        DECLARE_WAITQUEUE(wait, current);
                                        set_current_state(TASK_UNINTERRUPTIBLE);
                                        add_wait_queue(&c->erase_wait, &wait);
-                                       D1(printk(KERN_DEBUG "%s waiting for erase to complete\n", __func__));
+                                       jffs2_dbg(1, "%s waiting for erase to complete\n",
+                                                 __func__);
                                        spin_unlock(&c->erase_completion_lock);
 
                                        schedule();
@@ -144,7 +155,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
 
                ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
                if (ret) {
-                       D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
+                       jffs2_dbg(1, "%s(): ret is %d\n", __func__, ret);
                }
        }
        spin_unlock(&c->erase_completion_lock);
@@ -161,13 +172,14 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
        int ret = -EAGAIN;
        minsize = PAD(minsize);
 
-       D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
+       jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize);
 
        spin_lock(&c->erase_completion_lock);
        while(ret == -EAGAIN) {
                ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
                if (ret) {
-                       D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
+                       jffs2_dbg(1, "%s(): looping, ret is %d\n",
+                                 __func__, ret);
                }
        }
        spin_unlock(&c->erase_completion_lock);
@@ -184,8 +196,8 @@ static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblo
 {
 
        if (c->nextblock == NULL) {
-               D1(printk(KERN_DEBUG "jffs2_close_nextblock: Erase block at 0x%08x has already been placed in a list\n",
-                 jeb->offset));
+               jffs2_dbg(1, "%s(): Erase block at 0x%08x has already been placed in a list\n",
+                         __func__, jeb->offset);
                return;
        }
        /* Check, if we have a dirty block now, or if it was dirty already */
@@ -195,17 +207,20 @@ static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblo
                jeb->dirty_size += jeb->wasted_size;
                jeb->wasted_size = 0;
                if (VERYDIRTY(c, jeb->dirty_size)) {
-                       D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
-                         jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+                       jffs2_dbg(1, "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+                                 jeb->offset, jeb->free_size, jeb->dirty_size,
+                                 jeb->used_size);
                        list_add_tail(&jeb->list, &c->very_dirty_list);
                } else {
-                       D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
-                         jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+                       jffs2_dbg(1, "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+                                 jeb->offset, jeb->free_size, jeb->dirty_size,
+                                 jeb->used_size);
                        list_add_tail(&jeb->list, &c->dirty_list);
                }
        } else {
-               D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
-                 jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+               jffs2_dbg(1, "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+                         jeb->offset, jeb->free_size, jeb->dirty_size,
+                         jeb->used_size);
                list_add_tail(&jeb->list, &c->clean_list);
        }
        c->nextblock = NULL;
@@ -230,13 +245,14 @@ static int jffs2_find_nextblock(struct jffs2_sb_info *c)
                        list_move_tail(&ejeb->list, &c->erase_pending_list);
                        c->nr_erasing_blocks++;
                        jffs2_garbage_collect_trigger(c);
-                       D1(printk(KERN_DEBUG "jffs2_find_nextblock: Triggering erase of erasable block at 0x%08x\n",
-                                 ejeb->offset));
+                       jffs2_dbg(1, "%s(): Triggering erase of erasable block at 0x%08x\n",
+                                 __func__, ejeb->offset);
                }
 
                if (!c->nr_erasing_blocks &&
                        !list_empty(&c->erasable_pending_wbuf_list)) {
-                       D1(printk(KERN_DEBUG "jffs2_find_nextblock: Flushing write buffer\n"));
+                       jffs2_dbg(1, "%s(): Flushing write buffer\n",
+                                 __func__);
                        /* c->nextblock is NULL, no update to c->nextblock allowed */
                        spin_unlock(&c->erase_completion_lock);
                        jffs2_flush_wbuf_pad(c);
@@ -248,9 +264,11 @@ static int jffs2_find_nextblock(struct jffs2_sb_info *c)
                if (!c->nr_erasing_blocks) {
                        /* Ouch. We're in GC, or we wouldn't have got here.
                           And there's no space left. At all. */
-                       printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
-                                  c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no",
-                                  list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
+                       pr_crit("Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
+                               c->nr_erasing_blocks, c->nr_free_blocks,
+                               list_empty(&c->erasable_list) ? "yes" : "no",
+                               list_empty(&c->erasing_list) ? "yes" : "no",
+                               list_empty(&c->erase_pending_list) ? "yes" : "no");
                        return -ENOSPC;
                }
 
@@ -278,7 +296,8 @@ static int jffs2_find_nextblock(struct jffs2_sb_info *c)
                c->wbuf_ofs = 0xffffffff;
 #endif
 
-       D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset));
+       jffs2_dbg(1, "%s(): new nextblock = 0x%08x\n",
+                 __func__, c->nextblock->offset);
 
        return 0;
 }
@@ -345,7 +364,8 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
 
                        if (jffs2_wbuf_dirty(c)) {
                                spin_unlock(&c->erase_completion_lock);
-                               D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
+                               jffs2_dbg(1, "%s(): Flushing write buffer\n",
+                                         __func__);
                                jffs2_flush_wbuf_pad(c);
                                spin_lock(&c->erase_completion_lock);
                                jeb = c->nextblock;
@@ -387,7 +407,8 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
                jeb = c->nextblock;
 
                if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
-                       printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
+                       pr_warn("Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n",
+                               jeb->offset, jeb->free_size);
                        goto restart;
                }
        }
@@ -408,8 +429,9 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
                spin_lock(&c->erase_completion_lock);
        }
 
-       D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n",
-                 *len, jeb->offset + (c->sector_size - jeb->free_size)));
+       jffs2_dbg(1, "%s(): Giving 0x%x bytes at 0x%x\n",
+                 __func__,
+                 *len, jeb->offset + (c->sector_size - jeb->free_size));
        return 0;
 }
 
@@ -434,20 +456,22 @@ struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
 
        jeb = &c->blocks[ofs / c->sector_size];
 
-       D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n",
-                 ofs & ~3, ofs & 3, len));
+       jffs2_dbg(1, "%s(): Node at 0x%x(%d), size 0x%x\n",
+                 __func__, ofs & ~3, ofs & 3, len);
 #if 1
        /* Allow non-obsolete nodes only to be added at the end of c->nextblock, 
           if c->nextblock is set. Note that wbuf.c will file obsolete nodes
           even after refiling c->nextblock */
        if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE))
            && (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) {
-               printk(KERN_WARNING "argh. node added in wrong place at 0x%08x(%d)\n", ofs & ~3, ofs & 3);
+               pr_warn("argh. node added in wrong place at 0x%08x(%d)\n",
+                       ofs & ~3, ofs & 3);
                if (c->nextblock)
-                       printk(KERN_WARNING "nextblock 0x%08x", c->nextblock->offset);
+                       pr_warn("nextblock 0x%08x", c->nextblock->offset);
                else
-                       printk(KERN_WARNING "No nextblock");
-               printk(", expected at %08x\n", jeb->offset + (c->sector_size - jeb->free_size));
+                       pr_warn("No nextblock");
+               pr_cont(", expected at %08x\n",
+                       jeb->offset + (c->sector_size - jeb->free_size));
                return ERR_PTR(-EINVAL);
        }
 #endif
@@ -457,8 +481,9 @@ struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
 
        if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) {
                /* If it lives on the dirty_list, jffs2_reserve_space will put it there */
-               D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
-                         jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+               jffs2_dbg(1, "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+                         jeb->offset, jeb->free_size, jeb->dirty_size,
+                         jeb->used_size);
                if (jffs2_wbuf_dirty(c)) {
                        /* Flush the last write in the block if it's outstanding */
                        spin_unlock(&c->erase_completion_lock);
@@ -480,7 +505,7 @@ struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
 
 void jffs2_complete_reservation(struct jffs2_sb_info *c)
 {
-       D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n"));
+       jffs2_dbg(1, "jffs2_complete_reservation()\n");
        spin_lock(&c->erase_completion_lock);
        jffs2_garbage_collect_trigger(c);
        spin_unlock(&c->erase_completion_lock);
@@ -493,7 +518,7 @@ static inline int on_list(struct list_head *obj, struct list_head *head)
 
        list_for_each(this, head) {
                if (this == obj) {
-                       D1(printk("%p is on list at %p\n", obj, head));
+                       jffs2_dbg(1, "%p is on list at %p\n", obj, head);
                        return 1;
 
                }
@@ -511,16 +536,18 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
        uint32_t freed_len;
 
        if(unlikely(!ref)) {
-               printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
+               pr_notice("EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
                return;
        }
        if (ref_obsolete(ref)) {
-               D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref)));
+               jffs2_dbg(1, "%s(): called with already obsolete node at 0x%08x\n",
+                         __func__, ref_offset(ref));
                return;
        }
        blocknr = ref->flash_offset / c->sector_size;
        if (blocknr >= c->nr_blocks) {
-               printk(KERN_NOTICE "raw node at 0x%08x is off the end of device!\n", ref->flash_offset);
+               pr_notice("raw node at 0x%08x is off the end of device!\n",
+                         ref->flash_offset);
                BUG();
        }
        jeb = &c->blocks[blocknr];
@@ -542,27 +569,31 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 
        if (ref_flags(ref) == REF_UNCHECKED) {
                D1(if (unlikely(jeb->unchecked_size < freed_len)) {
-                       printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
-                              freed_len, blocknr, ref->flash_offset, jeb->used_size);
+                               pr_notice("raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
+                                         freed_len, blocknr,
+                                         ref->flash_offset, jeb->used_size);
                        BUG();
                })
-               D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), freed_len));
+                       jffs2_dbg(1, "Obsoleting previously unchecked node at 0x%08x of len %x\n",
+                                 ref_offset(ref), freed_len);
                jeb->unchecked_size -= freed_len;
                c->unchecked_size -= freed_len;
        } else {
                D1(if (unlikely(jeb->used_size < freed_len)) {
-                       printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
-                              freed_len, blocknr, ref->flash_offset, jeb->used_size);
+                               pr_notice("raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
+                                         freed_len, blocknr,
+                                         ref->flash_offset, jeb->used_size);
                        BUG();
                })
-               D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), freed_len));
+                       jffs2_dbg(1, "Obsoleting node at 0x%08x of len %#x: ",
+                                 ref_offset(ref), freed_len);
                jeb->used_size -= freed_len;
                c->used_size -= freed_len;
        }
 
        // Take care, that wasted size is taken into concern
        if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + freed_len)) && jeb != c->nextblock) {
-               D1(printk("Dirtying\n"));
+               jffs2_dbg(1, "Dirtying\n");
                addedsize = freed_len;
                jeb->dirty_size += freed_len;
                c->dirty_size += freed_len;
@@ -570,12 +601,12 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
                /* Convert wasted space to dirty, if not a bad block */
                if (jeb->wasted_size) {
                        if (on_list(&jeb->list, &c->bad_used_list)) {
-                               D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n",
-                                         jeb->offset));
+                               jffs2_dbg(1, "Leaving block at %08x on the bad_used_list\n",
+                                         jeb->offset);
                                addedsize = 0; /* To fool the refiling code later */
                        } else {
-                               D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n",
-                                         jeb->wasted_size, jeb->offset));
+                               jffs2_dbg(1, "Converting %d bytes of wasted space to dirty in block at %08x\n",
+                                         jeb->wasted_size, jeb->offset);
                                addedsize += jeb->wasted_size;
                                jeb->dirty_size += jeb->wasted_size;
                                c->dirty_size += jeb->wasted_size;
@@ -584,7 +615,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
                        }
                }
        } else {
-               D1(printk("Wasting\n"));
+               jffs2_dbg(1, "Wasting\n");
                addedsize = 0;
                jeb->wasted_size += freed_len;
                c->wasted_size += freed_len;
@@ -606,50 +637,57 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
        }
 
        if (jeb == c->nextblock) {
-               D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+               jffs2_dbg(2, "Not moving nextblock 0x%08x to dirty/erase_pending list\n",
+                         jeb->offset);
        } else if (!jeb->used_size && !jeb->unchecked_size) {
                if (jeb == c->gcblock) {
-                       D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset));
+                       jffs2_dbg(1, "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n",
+                                 jeb->offset);
                        c->gcblock = NULL;
                } else {
-                       D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
+                       jffs2_dbg(1, "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n",
+                                 jeb->offset);
                        list_del(&jeb->list);
                }
                if (jffs2_wbuf_dirty(c)) {
-                       D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
+                       jffs2_dbg(1, "...and adding to erasable_pending_wbuf_list\n");
                        list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
                } else {
                        if (jiffies & 127) {
                                /* Most of the time, we just erase it immediately. Otherwise we
                                   spend ages scanning it on mount, etc. */
-                               D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+                               jffs2_dbg(1, "...and adding to erase_pending_list\n");
                                list_add_tail(&jeb->list, &c->erase_pending_list);
                                c->nr_erasing_blocks++;
                                jffs2_garbage_collect_trigger(c);
                        } else {
                                /* Sometimes, however, we leave it elsewhere so it doesn't get
                                   immediately reused, and we spread the load a bit. */
-                               D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
+                               jffs2_dbg(1, "...and adding to erasable_list\n");
                                list_add_tail(&jeb->list, &c->erasable_list);
                        }
                }
-               D1(printk(KERN_DEBUG "Done OK\n"));
+               jffs2_dbg(1, "Done OK\n");
        } else if (jeb == c->gcblock) {
-               D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
+               jffs2_dbg(2, "Not moving gcblock 0x%08x to dirty_list\n",
+                         jeb->offset);
        } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) {
-               D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
+               jffs2_dbg(1, "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n",
+                         jeb->offset);
                list_del(&jeb->list);
-               D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
+               jffs2_dbg(1, "...and adding to dirty_list\n");
                list_add_tail(&jeb->list, &c->dirty_list);
        } else if (VERYDIRTY(c, jeb->dirty_size) &&
                   !VERYDIRTY(c, jeb->dirty_size - addedsize)) {
-               D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
+               jffs2_dbg(1, "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n",
+                         jeb->offset);
                list_del(&jeb->list);
-               D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
+               jffs2_dbg(1, "...and adding to very_dirty_list\n");
                list_add_tail(&jeb->list, &c->very_dirty_list);
        } else {
-               D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n",
-                         jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+               jffs2_dbg(1, "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n",
+                         jeb->offset, jeb->free_size, jeb->dirty_size,
+                         jeb->used_size);
        }
 
        spin_unlock(&c->erase_completion_lock);
@@ -665,33 +703,40 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
           the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
           by jffs2_free_jeb_node_refs() in erase.c. Which is nice. */
 
-       D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
+       jffs2_dbg(1, "obliterating obsoleted node at 0x%08x\n",
+                 ref_offset(ref));
        ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
        if (ret) {
-               printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
+               pr_warn("Read error reading from obsoleted node at 0x%08x: %d\n",
+                       ref_offset(ref), ret);
                goto out_erase_sem;
        }
        if (retlen != sizeof(n)) {
-               printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
+               pr_warn("Short read from obsoleted node at 0x%08x: %zd\n",
+                       ref_offset(ref), retlen);
                goto out_erase_sem;
        }
        if (PAD(je32_to_cpu(n.totlen)) != PAD(freed_len)) {
-               printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), freed_len);
+               pr_warn("Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n",
+                       je32_to_cpu(n.totlen), freed_len);
                goto out_erase_sem;
        }
        if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
-               D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
+               jffs2_dbg(1, "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n",
+                         ref_offset(ref), je16_to_cpu(n.nodetype));
                goto out_erase_sem;
        }
        /* XXX FIXME: This is ugly now */
        n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE);
        ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
        if (ret) {
-               printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
+               pr_warn("Write error in obliterating obsoleted node at 0x%08x: %d\n",
+                       ref_offset(ref), ret);
                goto out_erase_sem;
        }
        if (retlen != sizeof(n)) {
-               printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
+               pr_warn("Short write in obliterating obsoleted node at 0x%08x: %zd\n",
+                       ref_offset(ref), retlen);
                goto out_erase_sem;
        }
 
@@ -751,8 +796,8 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
                return 1;
 
        if (c->unchecked_size) {
-               D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
-                         c->unchecked_size, c->checked_ino));
+               jffs2_dbg(1, "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
+                         c->unchecked_size, c->checked_ino);
                return 1;
        }
 
@@ -780,8 +825,9 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
                }
        }
 
-       D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x, vdirty_blocks %d: %s\n",
-                 c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, nr_very_dirty, ret?"yes":"no"));
+       jffs2_dbg(1, "%s(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x, vdirty_blocks %d: %s\n",
+                 __func__, c->nr_free_blocks, c->nr_erasing_blocks,
+                 c->dirty_size, nr_very_dirty, ret ? "yes" : "no");
 
        return ret;
 }
index ab65ee3ec858e9ad7a810a5174242c6e4222dcae..1cd3aec9d9ae282dd31226d0717aaf69a55f414d 100644 (file)
@@ -76,7 +76,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
 #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
 
 #define jffs2_flash_write(c, ofs, len, retlen, buf) jffs2_flash_direct_write(c, ofs, len, retlen, buf)
-#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
+#define jffs2_flash_read(c, ofs, len, retlen, buf) (mtd_read((c)->mtd, ofs, len, retlen, buf))
 #define jffs2_flush_wbuf_pad(c) ({ do{} while(0); (void)(c), 0; })
 #define jffs2_flush_wbuf_gc(c, i) ({ do{} while(0); (void)(c), (void) i, 0; })
 #define jffs2_write_nand_badblock(c,jeb,bad_offset) (1)
@@ -108,8 +108,6 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
 
 #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
 
-#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
-#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
 #define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
 
 /* wbuf.c */
index 3f39be1b0455a03be83b8cb08bfa224a6dd75d7b..0b042b1fc82fea33eed64866617e0bd67aafa0ba 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/crc32.h>
@@ -36,24 +38,25 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
        ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
        if (ret) {
                jffs2_free_raw_inode(ri);
-               printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret);
+               pr_warn("Error reading node from 0x%08x: %d\n",
+                       ref_offset(fd->raw), ret);
                return ret;
        }
        if (readlen != sizeof(*ri)) {
                jffs2_free_raw_inode(ri);
-               printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n",
-                      ref_offset(fd->raw), sizeof(*ri), readlen);
+               pr_warn("Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n",
+                       ref_offset(fd->raw), sizeof(*ri), readlen);
                return -EIO;
        }
        crc = crc32(0, ri, sizeof(*ri)-8);
 
-       D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
+       jffs2_dbg(1, "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
                  ref_offset(fd->raw), je32_to_cpu(ri->node_crc),
                  crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
-                 je32_to_cpu(ri->offset), buf));
+                 je32_to_cpu(ri->offset), buf);
        if (crc != je32_to_cpu(ri->node_crc)) {
-               printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n",
-                      je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
+               pr_warn("Node CRC %08x != calculated CRC %08x for node at %08x\n",
+                       je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
                ret = -EIO;
                goto out_ri;
        }
@@ -66,8 +69,8 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
        }
 
        D1(if(ofs + len > je32_to_cpu(ri->dsize)) {
-               printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
-                      len, ofs, je32_to_cpu(ri->dsize));
+                       pr_warn("jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
+                               len, ofs, je32_to_cpu(ri->dsize));
                ret = -EINVAL;
                goto out_ri;
        });
@@ -107,8 +110,8 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                decomprbuf = readbuf;
        }
 
-       D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
-                 readbuf));
+       jffs2_dbg(2, "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
+                 readbuf);
        ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri),
                               je32_to_cpu(ri->csize), &readlen, readbuf);
 
@@ -119,18 +122,19 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 
        crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
        if (crc != je32_to_cpu(ri->data_crc)) {
-               printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n",
-                      je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
+               pr_warn("Data CRC %08x != calculated CRC %08x for node at %08x\n",
+                       je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
                ret = -EIO;
                goto out_decomprbuf;
        }
-       D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc));
+       jffs2_dbg(2, "Data CRC matches calculated CRC %08x\n", crc);
        if (ri->compr != JFFS2_COMPR_NONE) {
-               D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
-                         je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf));
+               jffs2_dbg(2, "Decompress %d bytes from %p to %d bytes at %p\n",
+                         je32_to_cpu(ri->csize), readbuf,
+                         je32_to_cpu(ri->dsize), decomprbuf);
                ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
                if (ret) {
-                       printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
+                       pr_warn("Error: jffs2_decompress returned %d\n", ret);
                        goto out_decomprbuf;
                }
        }
@@ -157,8 +161,8 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
        struct jffs2_node_frag *frag;
        int ret;
 
-       D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
-                 f->inocache->ino, offset, offset+len));
+       jffs2_dbg(1, "%s(): ino #%u, range 0x%08x-0x%08x\n",
+                 __func__, f->inocache->ino, offset, offset + len);
 
        frag = jffs2_lookup_node_frag(&f->fragtree, offset);
 
@@ -168,22 +172,27 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
         * (or perhaps is before it, if we've been asked to read off the
         * end of the file). */
        while(offset < end) {
-               D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end));
+               jffs2_dbg(2, "%s(): offset %d, end %d\n",
+                         __func__, offset, end);
                if (unlikely(!frag || frag->ofs > offset ||
                             frag->ofs + frag->size <= offset)) {
                        uint32_t holesize = end - offset;
                        if (frag && frag->ofs > offset) {
-                               D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset));
+                               jffs2_dbg(1, "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
+                                         f->inocache->ino, frag->ofs, offset);
                                holesize = min(holesize, frag->ofs - offset);
                        }
-                       D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
+                       jffs2_dbg(1, "Filling non-frag hole from %d-%d\n",
+                                 offset, offset + holesize);
                        memset(buf, 0, holesize);
                        buf += holesize;
                        offset += holesize;
                        continue;
                } else if (unlikely(!frag->node)) {
                        uint32_t holeend = min(end, frag->ofs + frag->size);
-                       D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
+                       jffs2_dbg(1, "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n",
+                                 offset, holeend, frag->ofs,
+                                 frag->ofs + frag->size);
                        memset(buf, 0, holeend - offset);
                        buf += holeend - offset;
                        offset = holeend;
@@ -195,20 +204,23 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 
                        fragofs = offset - frag->ofs;
                        readlen = min(frag->size - fragofs, end - offset);
-                       D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
-                                 frag->ofs+fragofs, frag->ofs+fragofs+readlen,
-                                 ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
+                       jffs2_dbg(1, "Reading %d-%d from node at 0x%08x (%d)\n",
+                                 frag->ofs+fragofs,
+                                 frag->ofs + fragofs+readlen,
+                                 ref_offset(frag->node->raw),
+                                 ref_flags(frag->node->raw));
                        ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
-                       D2(printk(KERN_DEBUG "node read done\n"));
+                       jffs2_dbg(2, "node read done\n");
                        if (ret) {
-                               D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
+                               jffs2_dbg(1, "%s(): error %d\n",
+                                         __func__, ret);
                                memset(buf, 0, readlen);
                                return ret;
                        }
                        buf += readlen;
                        offset += readlen;
                        frag = frag_next(frag);
-                       D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
+                       jffs2_dbg(2, "node read was OK. Looping\n");
                }
        }
        return 0;
index 3093ac4fb24c2966c39b4987040dc9b5a3c33855..dc0437e8476322aaff40dc01737dcc2cabdc6976 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
index f99464833bb2fb6e1ce886a713706b0bd88413ff..7654e87b042869ef43aff269a10e88a4088d59c3 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 
 #define DEFAULT_EMPTY_SCAN_SIZE 256
 
-#define noisy_printk(noise, args...) do { \
-       if (*(noise)) { \
-               printk(KERN_NOTICE args); \
-                (*(noise))--; \
-                if (!(*(noise))) { \
-                        printk(KERN_NOTICE "Further such events for this erase block will not be printed\n"); \
-                } \
-       } \
-} while(0)
+#define noisy_printk(noise, fmt, ...)                                  \
+do {                                                                   \
+       if (*(noise)) {                                                 \
+               pr_notice(fmt, ##__VA_ARGS__);                          \
+               (*(noise))--;                                           \
+               if (!(*(noise)))                                        \
+                       pr_notice("Further such events for this erase block will not be printed\n"); \
+       }                                                               \
+} while (0)
 
 static uint32_t pseudo_random;
 
@@ -96,18 +98,17 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
 #ifndef __ECOS
        size_t pointlen, try_size;
 
-       if (c->mtd->point) {
-               ret = mtd_point(c->mtd, 0, c->mtd->size, &pointlen,
-                               (void **)&flashbuf, NULL);
-               if (!ret && pointlen < c->mtd->size) {
-                       /* Don't muck about if it won't let us point to the whole flash */
-                       D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
-                       mtd_unpoint(c->mtd, 0, pointlen);
-                       flashbuf = NULL;
-               }
-               if (ret && ret != -EOPNOTSUPP)
-                       D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
+       ret = mtd_point(c->mtd, 0, c->mtd->size, &pointlen,
+                       (void **)&flashbuf, NULL);
+       if (!ret && pointlen < c->mtd->size) {
+               /* Don't muck about if it won't let us point to the whole flash */
+               jffs2_dbg(1, "MTD point returned len too short: 0x%zx\n",
+                         pointlen);
+               mtd_unpoint(c->mtd, 0, pointlen);
+               flashbuf = NULL;
        }
+       if (ret && ret != -EOPNOTSUPP)
+               jffs2_dbg(1, "MTD point failed %d\n", ret);
 #endif
        if (!flashbuf) {
                /* For NAND it's quicker to read a whole eraseblock at a time,
@@ -117,15 +118,15 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
                else
                        try_size = PAGE_SIZE;
 
-               D1(printk(KERN_DEBUG "Trying to allocate readbuf of %zu "
-                       "bytes\n", try_size));
+               jffs2_dbg(1, "Trying to allocate readbuf of %zu "
+                         "bytes\n", try_size);
 
                flashbuf = mtd_kmalloc_up_to(c->mtd, &try_size);
                if (!flashbuf)
                        return -ENOMEM;
 
-               D1(printk(KERN_DEBUG "Allocated readbuf of %zu bytes\n",
-                       try_size));
+               jffs2_dbg(1, "Allocated readbuf of %zu bytes\n",
+                         try_size);
 
                buf_size = (uint32_t)try_size;
        }
@@ -178,7 +179,8 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
                                c->nr_free_blocks++;
                        } else {
                                /* Dirt */
-                               D1(printk(KERN_DEBUG "Adding all-dirty block at 0x%08x to erase_pending_list\n", jeb->offset));
+                               jffs2_dbg(1, "Adding all-dirty block at 0x%08x to erase_pending_list\n",
+                                         jeb->offset);
                                list_add(&jeb->list, &c->erase_pending_list);
                                c->nr_erasing_blocks++;
                        }
@@ -205,7 +207,8 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
                                }
                                /* update collected summary information for the current nextblock */
                                jffs2_sum_move_collected(c, s);
-                               D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset));
+                               jffs2_dbg(1, "%s(): new nextblock = 0x%08x\n",
+                                         __func__, jeb->offset);
                                c->nextblock = jeb;
                        } else {
                                ret = file_dirty(c, jeb);
@@ -217,20 +220,21 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
                case BLK_STATE_ALLDIRTY:
                        /* Nothing valid - not even a clean marker. Needs erasing. */
                        /* For now we just put it on the erasing list. We'll start the erases later */
-                       D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
+                       jffs2_dbg(1, "Erase block at 0x%08x is not formatted. It will be erased\n",
+                                 jeb->offset);
                        list_add(&jeb->list, &c->erase_pending_list);
                        c->nr_erasing_blocks++;
                        break;
 
                case BLK_STATE_BADBLOCK:
-                       D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
+                       jffs2_dbg(1, "Block at 0x%08x is bad\n", jeb->offset);
                        list_add(&jeb->list, &c->bad_list);
                        c->bad_size += c->sector_size;
                        c->free_size -= c->sector_size;
                        bad_blocks++;
                        break;
                default:
-                       printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n");
+                       pr_warn("%s(): unknown block state\n", __func__);
                        BUG();
                }
        }
@@ -250,16 +254,17 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
 
                uint32_t skip = c->nextblock->free_size % c->wbuf_pagesize;
 
-               D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
-                         skip));
+               jffs2_dbg(1, "%s(): Skipping %d bytes in nextblock to ensure page alignment\n",
+                         __func__, skip);
                jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
                jffs2_scan_dirty_space(c, c->nextblock, skip);
        }
 #endif
        if (c->nr_erasing_blocks) {
                if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
-                       printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
-                       printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
+                       pr_notice("Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
+                       pr_notice("empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",
+                                 empty_blocks, bad_blocks, c->nr_blocks);
                        ret = -EIO;
                        goto out;
                }
@@ -287,11 +292,13 @@ static int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf,
 
        ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
        if (ret) {
-               D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret));
+               jffs2_dbg(1, "mtd->read(0x%x bytes from 0x%x) returned %d\n",
+                         len, ofs, ret);
                return ret;
        }
        if (retlen < len) {
-               D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen));
+               jffs2_dbg(1, "Read at 0x%x gave only 0x%zx bytes\n",
+                         ofs, retlen);
                return -EIO;
        }
        return 0;
@@ -368,7 +375,7 @@ static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
 
        if (jffs2_sum_active())
                jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset);
-       dbg_xattr("scaning xdatum at %#08x (xid=%u, version=%u)\n",
+       dbg_xattr("scanning xdatum at %#08x (xid=%u, version=%u)\n",
                  ofs, xd->xid, xd->version);
        return 0;
 }
@@ -449,7 +456,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
        ofs = jeb->offset;
        prevofs = jeb->offset - 1;
 
-       D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
+       jffs2_dbg(1, "%s(): Scanning block at 0x%x\n", __func__, ofs);
 
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
        if (jffs2_cleanmarker_oob(c)) {
@@ -459,7 +466,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
                        return BLK_STATE_BADBLOCK;
 
                ret = jffs2_check_nand_cleanmarker(c, jeb);
-               D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
+               jffs2_dbg(2, "jffs_check_nand_cleanmarker returned %d\n", ret);
 
                /* Even if it's not found, we still scan to see
                   if the block is empty. We use this information
@@ -561,7 +568,8 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
                if (jffs2_cleanmarker_oob(c)) {
                        /* scan oob, take care of cleanmarker */
                        int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
-                       D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret));
+                       jffs2_dbg(2, "jffs2_check_oob_empty returned %d\n",
+                                 ret);
                        switch (ret) {
                        case 0:         return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
                        case 1:         return BLK_STATE_ALLDIRTY;
@@ -569,15 +577,16 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
                        }
                }
 #endif
-               D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
+               jffs2_dbg(1, "Block at 0x%08x is empty (erased)\n",
+                         jeb->offset);
                if (c->cleanmarker_size == 0)
                        return BLK_STATE_CLEANMARKER;   /* don't bother with re-erase */
                else
                        return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
        }
        if (ofs) {
-               D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
-                         jeb->offset + ofs));
+               jffs2_dbg(1, "Free space at %08x ends at %08x\n", jeb->offset,
+                         jeb->offset + ofs);
                if ((err = jffs2_prealloc_raw_node_refs(c, jeb, 1)))
                        return err;
                if ((err = jffs2_scan_dirty_space(c, jeb, ofs)))
@@ -604,12 +613,13 @@ scan_more:
                cond_resched();
 
                if (ofs & 3) {
-                       printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs);
+                       pr_warn("Eep. ofs 0x%08x not word-aligned!\n", ofs);
                        ofs = PAD(ofs);
                        continue;
                }
                if (ofs == prevofs) {
-                       printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs);
+                       pr_warn("ofs 0x%08x has already been seen. Skipping\n",
+                               ofs);
                        if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
                                return err;
                        ofs += 4;
@@ -618,8 +628,10 @@ scan_more:
                prevofs = ofs;
 
                if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
-                       D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
-                                 jeb->offset, c->sector_size, ofs, sizeof(*node)));
+                       jffs2_dbg(1, "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n",
+                                 sizeof(struct jffs2_unknown_node),
+                                 jeb->offset, c->sector_size, ofs,
+                                 sizeof(*node));
                        if ((err = jffs2_scan_dirty_space(c, jeb, (jeb->offset + c->sector_size)-ofs)))
                                return err;
                        break;
@@ -627,8 +639,9 @@ scan_more:
 
                if (buf_ofs + buf_len < ofs + sizeof(*node)) {
                        buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
-                       D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
-                                 sizeof(struct jffs2_unknown_node), buf_len, ofs));
+                       jffs2_dbg(1, "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
+                                 sizeof(struct jffs2_unknown_node),
+                                 buf_len, ofs);
                        err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
                        if (err)
                                return err;
@@ -645,13 +658,13 @@ scan_more:
                        ofs += 4;
                        scan_end = min_t(uint32_t, EMPTY_SCAN_SIZE(c->sector_size)/8, buf_len);
 
-                       D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
+                       jffs2_dbg(1, "Found empty flash at 0x%08x\n", ofs);
                more_empty:
                        inbuf_ofs = ofs - buf_ofs;
                        while (inbuf_ofs < scan_end) {
                                if (unlikely(*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)) {
-                                       printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
-                                              empty_start, ofs);
+                                       pr_warn("Empty flash at 0x%08x ends at 0x%08x\n",
+                                               empty_start, ofs);
                                        if ((err = jffs2_scan_dirty_space(c, jeb, ofs-empty_start)))
                                                return err;
                                        goto scan_more;
@@ -661,13 +674,15 @@ scan_more:
                                ofs += 4;
                        }
                        /* Ran off end. */
-                       D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
+                       jffs2_dbg(1, "Empty flash to end of buffer at 0x%08x\n",
+                                 ofs);
 
                        /* If we're only checking the beginning of a block with a cleanmarker,
                           bail now */
                        if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
                            c->cleanmarker_size && !jeb->dirty_size && !ref_next(jeb->first_node)) {
-                               D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
+                               jffs2_dbg(1, "%d bytes at start of block seems clean... assuming all clean\n",
+                                         EMPTY_SCAN_SIZE(c->sector_size));
                                return BLK_STATE_CLEANMARKER;
                        }
                        if (!buf_size && (scan_end != buf_len)) {/* XIP/point case */
@@ -680,13 +695,14 @@ scan_more:
                        if (!buf_len) {
                                /* No more to read. Break out of main loop without marking
                                   this range of empty space as dirty (because it's not) */
-                               D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
-                                         empty_start));
+                               jffs2_dbg(1, "Empty flash at %08x runs to end of block. Treating as free_space\n",
+                                         empty_start);
                                break;
                        }
                        /* point never reaches here */
                        scan_end = buf_len;
-                       D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
+                       jffs2_dbg(1, "Reading another 0x%x at 0x%08x\n",
+                                 buf_len, ofs);
                        err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
                        if (err)
                                return err;
@@ -695,22 +711,23 @@ scan_more:
                }
 
                if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
-                       printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
+                       pr_warn("Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n",
+                               ofs);
                        if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
                                return err;
                        ofs += 4;
                        continue;
                }
                if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
-                       D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
+                       jffs2_dbg(1, "Dirty bitmask at 0x%08x\n", ofs);
                        if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
                                return err;
                        ofs += 4;
                        continue;
                }
                if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
-                       printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
-                       printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
+                       pr_warn("Old JFFS2 bitmask found at 0x%08x\n", ofs);
+                       pr_warn("You cannot use older JFFS2 filesystems with newer kernels\n");
                        if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
                                return err;
                        ofs += 4;
@@ -718,7 +735,8 @@ scan_more:
                }
                if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {
                        /* OK. We're out of possibilities. Whinge and move on */
-                       noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
+                       noisy_printk(&noise, "%s(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
+                                    __func__,
                                     JFFS2_MAGIC_BITMASK, ofs,
                                     je16_to_cpu(node->magic));
                        if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
@@ -733,7 +751,8 @@ scan_more:
                hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4);
 
                if (hdr_crc != je32_to_cpu(node->hdr_crc)) {
-                       noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
+                       noisy_printk(&noise, "%s(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
+                                    __func__,
                                     ofs, je16_to_cpu(node->magic),
                                     je16_to_cpu(node->nodetype),
                                     je32_to_cpu(node->totlen),
@@ -747,9 +766,9 @@ scan_more:
 
                if (ofs + je32_to_cpu(node->totlen) > jeb->offset + c->sector_size) {
                        /* Eep. Node goes over the end of the erase block. */
-                       printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
-                              ofs, je32_to_cpu(node->totlen));
-                       printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
+                       pr_warn("Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
+                               ofs, je32_to_cpu(node->totlen));
+                       pr_warn("Perhaps the file system was created with the wrong erase size?\n");
                        if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
                                return err;
                        ofs += 4;
@@ -758,7 +777,8 @@ scan_more:
 
                if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
                        /* Wheee. This is an obsoleted node */
-                       D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
+                       jffs2_dbg(2, "Node at 0x%08x is obsolete. Skipping\n",
+                                 ofs);
                        if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
                                return err;
                        ofs += PAD(je32_to_cpu(node->totlen));
@@ -769,8 +789,9 @@ scan_more:
                case JFFS2_NODETYPE_INODE:
                        if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
                                buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
-                               D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
-                                         sizeof(struct jffs2_raw_inode), buf_len, ofs));
+                               jffs2_dbg(1, "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
+                                         sizeof(struct jffs2_raw_inode),
+                                         buf_len, ofs);
                                err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
                                if (err)
                                        return err;
@@ -785,8 +806,9 @@ scan_more:
                case JFFS2_NODETYPE_DIRENT:
                        if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
                                buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
-                               D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
-                                         je32_to_cpu(node->totlen), buf_len, ofs));
+                               jffs2_dbg(1, "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
+                                         je32_to_cpu(node->totlen), buf_len,
+                                         ofs);
                                err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
                                if (err)
                                        return err;
@@ -802,9 +824,9 @@ scan_more:
                case JFFS2_NODETYPE_XATTR:
                        if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
                                buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
-                               D1(printk(KERN_DEBUG "Fewer than %d bytes (xattr node)"
-                                         " left to end of buf. Reading 0x%x at 0x%08x\n",
-                                         je32_to_cpu(node->totlen), buf_len, ofs));
+                               jffs2_dbg(1, "Fewer than %d bytes (xattr node) left to end of buf. Reading 0x%x at 0x%08x\n",
+                                         je32_to_cpu(node->totlen), buf_len,
+                                         ofs);
                                err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
                                if (err)
                                        return err;
@@ -819,9 +841,9 @@ scan_more:
                case JFFS2_NODETYPE_XREF:
                        if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
                                buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
-                               D1(printk(KERN_DEBUG "Fewer than %d bytes (xref node)"
-                                         " left to end of buf. Reading 0x%x at 0x%08x\n",
-                                         je32_to_cpu(node->totlen), buf_len, ofs));
+                               jffs2_dbg(1, "Fewer than %d bytes (xref node) left to end of buf. Reading 0x%x at 0x%08x\n",
+                                         je32_to_cpu(node->totlen), buf_len,
+                                         ofs);
                                err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
                                if (err)
                                        return err;
@@ -836,15 +858,17 @@ scan_more:
 #endif /* CONFIG_JFFS2_FS_XATTR */
 
                case JFFS2_NODETYPE_CLEANMARKER:
-                       D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
+                       jffs2_dbg(1, "CLEANMARKER node found at 0x%08x\n", ofs);
                        if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
-                               printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
-                                      ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
+                               pr_notice("CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+                                         ofs, je32_to_cpu(node->totlen),
+                                         c->cleanmarker_size);
                                if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
                                        return err;
                                ofs += PAD(sizeof(struct jffs2_unknown_node));
                        } else if (jeb->first_node) {
-                               printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
+                               pr_notice("CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n",
+                                         ofs, jeb->offset);
                                if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
                                        return err;
                                ofs += PAD(sizeof(struct jffs2_unknown_node));
@@ -866,7 +890,8 @@ scan_more:
                default:
                        switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
                        case JFFS2_FEATURE_ROCOMPAT:
-                               printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
+                               pr_notice("Read-only compatible feature node (0x%04x) found at offset 0x%08x\n",
+                                         je16_to_cpu(node->nodetype), ofs);
                                c->flags |= JFFS2_SB_FLAG_RO;
                                if (!(jffs2_is_readonly(c)))
                                        return -EROFS;
@@ -876,18 +901,21 @@ scan_more:
                                break;
 
                        case JFFS2_FEATURE_INCOMPAT:
-                               printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
+                               pr_notice("Incompatible feature node (0x%04x) found at offset 0x%08x\n",
+                                         je16_to_cpu(node->nodetype), ofs);
                                return -EINVAL;
 
                        case JFFS2_FEATURE_RWCOMPAT_DELETE:
-                               D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
+                               jffs2_dbg(1, "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n",
+                                         je16_to_cpu(node->nodetype), ofs);
                                if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
                                        return err;
                                ofs += PAD(je32_to_cpu(node->totlen));
                                break;
 
                        case JFFS2_FEATURE_RWCOMPAT_COPY: {
-                               D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
+                               jffs2_dbg(1, "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n",
+                                         je16_to_cpu(node->nodetype), ofs);
 
                                jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(node->totlen)), NULL);
 
@@ -908,8 +936,9 @@ scan_more:
                }
        }
 
-       D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n",
-                 jeb->offset,jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size, jeb->wasted_size));
+       jffs2_dbg(1, "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n",
+                 jeb->offset, jeb->free_size, jeb->dirty_size,
+                 jeb->unchecked_size, jeb->used_size, jeb->wasted_size);
        
        /* mark_node_obsolete can add to wasted !! */
        if (jeb->wasted_size) {
@@ -935,7 +964,7 @@ struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uin
 
        ic = jffs2_alloc_inode_cache();
        if (!ic) {
-               printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
+               pr_notice("%s(): allocation of inode cache failed\n", __func__);
                return NULL;
        }
        memset(ic, 0, sizeof(*ic));
@@ -954,7 +983,7 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
        struct jffs2_inode_cache *ic;
        uint32_t crc, ino = je32_to_cpu(ri->ino);
 
-       D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
+       jffs2_dbg(1, "%s(): Node at 0x%08x\n", __func__, ofs);
 
        /* We do very little here now. Just check the ino# to which we should attribute
           this node; we can do all the CRC checking etc. later. There's a tradeoff here --
@@ -968,9 +997,8 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
        /* Check the node CRC in any case. */
        crc = crc32(0, ri, sizeof(*ri)-8);
        if (crc != je32_to_cpu(ri->node_crc)) {
-               printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on "
-                      "node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                      ofs, je32_to_cpu(ri->node_crc), crc);
+               pr_notice("%s(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+                         __func__, ofs, je32_to_cpu(ri->node_crc), crc);
                /*
                 * We believe totlen because the CRC on the node
                 * _header_ was OK, just the node itself failed.
@@ -989,10 +1017,10 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
        /* Wheee. It worked */
        jffs2_link_node_ref(c, jeb, ofs | REF_UNCHECKED, PAD(je32_to_cpu(ri->totlen)), ic);
 
-       D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
+       jffs2_dbg(1, "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
                  je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
                  je32_to_cpu(ri->offset),
-                 je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize)));
+                 je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize));
 
        pseudo_random += je32_to_cpu(ri->version);
 
@@ -1012,15 +1040,15 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
        uint32_t crc;
        int err;
 
-       D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
+       jffs2_dbg(1, "%s(): Node at 0x%08x\n", __func__, ofs);
 
        /* We don't get here unless the node is still valid, so we don't have to
           mask in the ACCURATE bit any more. */
        crc = crc32(0, rd, sizeof(*rd)-8);
 
        if (crc != je32_to_cpu(rd->node_crc)) {
-               printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                      ofs, je32_to_cpu(rd->node_crc), crc);
+               pr_notice("%s(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+                         __func__, ofs, je32_to_cpu(rd->node_crc), crc);
                /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
                if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen)))))
                        return err;
@@ -1032,7 +1060,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
        /* Should never happen. Did. (OLPC trac #4184)*/
        checkedlen = strnlen(rd->name, rd->nsize);
        if (checkedlen < rd->nsize) {
-               printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n",
+               pr_err("Dirent at %08x has zeroes in name. Truncating to %d chars\n",
                       ofs, checkedlen);
        }
        fd = jffs2_alloc_full_dirent(checkedlen+1);
@@ -1044,9 +1072,10 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
 
        crc = crc32(0, fd->name, rd->nsize);
        if (crc != je32_to_cpu(rd->name_crc)) {
-               printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                      ofs, je32_to_cpu(rd->name_crc), crc);
-               D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino)));
+               pr_notice("%s(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+                         __func__, ofs, je32_to_cpu(rd->name_crc), crc);
+               jffs2_dbg(1, "Name for which CRC failed is (now) '%s', ino #%d\n",
+                         fd->name, je32_to_cpu(rd->ino));
                jffs2_free_full_dirent(fd);
                /* FIXME: Why do we believe totlen? */
                /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
index 0f20208df60278c9f63f6c14c7b626cfd632ebf2..aca97f35b292f71be6a1a01007780c25a990551c 100644 (file)
@@ -23,8 +23,8 @@
 #include "nodelist.h"
 
 /* ---- Initial Security Label(s) Attachment callback --- */
-int jffs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
-                    void *fs_info)
+static int jffs2_initxattrs(struct inode *inode,
+                           const struct xattr *xattr_array, void *fs_info)
 {
        const struct xattr *xattr;
        int err = 0;
index e537fb0e0184e97eae8907a97c34d46920d9f129..c522d098bb4fb622112b069c6b5556d7f31ef46a 100644 (file)
@@ -11,6 +11,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
@@ -442,13 +444,16 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
                                /* This should never happen, but https://dev.laptop.org/ticket/4184 */
                                checkedlen = strnlen(spd->name, spd->nsize);
                                if (!checkedlen) {
-                                       printk(KERN_ERR "Dirent at %08x has zero at start of name. Aborting mount.\n",
-                                              jeb->offset + je32_to_cpu(spd->offset));
+                                       pr_err("Dirent at %08x has zero at start of name. Aborting mount.\n",
+                                              jeb->offset +
+                                              je32_to_cpu(spd->offset));
                                        return -EIO;
                                }
                                if (checkedlen < spd->nsize) {
-                                       printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n",
-                                              jeb->offset + je32_to_cpu(spd->offset), checkedlen);
+                                       pr_err("Dirent at %08x has zeroes in name. Truncating to %d chars\n",
+                                              jeb->offset +
+                                              je32_to_cpu(spd->offset),
+                                              checkedlen);
                                }
 
 
@@ -808,8 +813,7 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
 
        sum_ofs = jeb->offset + c->sector_size - jeb->free_size;
 
-       dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n",
-                   sum_ofs);
+       dbg_summary("writing out data to flash to pos : 0x%08x\n", sum_ofs);
 
        ret = jffs2_flash_writev(c, vecs, 2, sum_ofs, &retlen, 0);
 
index f2d96b5e64f6fa47ee8fd11543156c926779ca0e..f9916f312bd81e3590fde1c92a025458cb64ab11 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -69,7 +71,7 @@ static void jffs2_write_super(struct super_block *sb)
        sb->s_dirt = 0;
 
        if (!(sb->s_flags & MS_RDONLY)) {
-               D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
+               jffs2_dbg(1, "%s()\n", __func__);
                jffs2_flush_wbuf_gc(c, 0);
        }
 
@@ -214,8 +216,8 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data)
                                                JFFS2_COMPR_MODE_FORCEZLIB;
 #endif
                        else {
-                               printk(KERN_ERR "JFFS2 Error: unknown compressor \"%s\"",
-                                               name);
+                               pr_err("Error: unknown compressor \"%s\"\n",
+                                      name);
                                kfree(name);
                                return -EINVAL;
                        }
@@ -223,8 +225,8 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data)
                        c->mount_opts.override_compr = true;
                        break;
                default:
-                       printk(KERN_ERR "JFFS2 Error: unrecognized mount option '%s' or missing value\n",
-                                       p);
+                       pr_err("Error: unrecognized mount option '%s' or missing value\n",
+                              p);
                        return -EINVAL;
                }
        }
@@ -266,9 +268,9 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent)
        struct jffs2_sb_info *c;
        int ret;
 
-       D1(printk(KERN_DEBUG "jffs2_get_sb_mtd():"
+       jffs2_dbg(1, "jffs2_get_sb_mtd():"
                  " New superblock for device %d (\"%s\")\n",
-                 sb->s_mtd->index, sb->s_mtd->name));
+                 sb->s_mtd->index, sb->s_mtd->name);
 
        c = kzalloc(sizeof(*c), GFP_KERNEL);
        if (!c)
@@ -315,7 +317,7 @@ static void jffs2_put_super (struct super_block *sb)
 {
        struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 
-       D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
+       jffs2_dbg(2, "%s()\n", __func__);
 
        if (sb->s_dirt)
                jffs2_write_super(sb);
@@ -336,7 +338,7 @@ static void jffs2_put_super (struct super_block *sb)
        kfree(c->inocache_list);
        jffs2_clear_xattr_subsystem(c);
        mtd_sync(c->mtd);
-       D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
+       jffs2_dbg(1, "%s(): returning\n", __func__);
 }
 
 static void jffs2_kill_sb(struct super_block *sb)
@@ -371,7 +373,7 @@ static int __init init_jffs2_fs(void)
        BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68);
        BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32);
 
-       printk(KERN_INFO "JFFS2 version 2.2."
+       pr_info("version 2.2."
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
               " (NAND)"
 #endif
@@ -386,22 +388,22 @@ static int __init init_jffs2_fs(void)
                                                SLAB_MEM_SPREAD),
                                             jffs2_i_init_once);
        if (!jffs2_inode_cachep) {
-               printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
+               pr_err("error: Failed to initialise inode cache\n");
                return -ENOMEM;
        }
        ret = jffs2_compressors_init();
        if (ret) {
-               printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
+               pr_err("error: Failed to initialise compressors\n");
                goto out;
        }
        ret = jffs2_create_slab_caches();
        if (ret) {
-               printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
+               pr_err("error: Failed to initialise slab caches\n");
                goto out_compressors;
        }
        ret = register_filesystem(&jffs2_fs_type);
        if (ret) {
-               printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
+               pr_err("error: Failed to register filesystem\n");
                goto out_slab;
        }
        return 0;
index e3035afb18145e60add43a86640b1464fdac5aa4..6e563332bb242e2d4214026fa4ff608bcd69cf94 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/namei.h>
@@ -47,10 +49,11 @@ static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
         */
 
        if (!p) {
-               printk(KERN_ERR "jffs2_follow_link(): can't find symlink target\n");
+               pr_err("%s(): can't find symlink target\n", __func__);
                p = ERR_PTR(-EIO);
        }
-       D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target));
+       jffs2_dbg(1, "%s(): target path is '%s'\n",
+                 __func__, (char *)f->target);
 
        nd_set_link(nd, p);
 
index 30e8f47e8a233f32ad7eaab70dfbf1291bcf409b..74d9be19df3f1fff1d7defdc7824c90240a302f6 100644 (file)
@@ -11,6 +11,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
@@ -91,7 +93,7 @@ static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
 
        new = kmalloc(sizeof(*new), GFP_KERNEL);
        if (!new) {
-               D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n"));
+               jffs2_dbg(1, "No memory to allocate inodirty. Fallback to all considered dirty\n");
                jffs2_clear_wbuf_ino_list(c);
                c->wbuf_inodes = &inodirty_nomem;
                return;
@@ -113,19 +115,20 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
        list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
                struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
 
-               D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset));
+               jffs2_dbg(1, "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n",
+                         jeb->offset);
                list_del(this);
                if ((jiffies + (n++)) & 127) {
                        /* Most of the time, we just erase it immediately. Otherwise we
                           spend ages scanning it on mount, etc. */
-                       D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+                       jffs2_dbg(1, "...and adding to erase_pending_list\n");
                        list_add_tail(&jeb->list, &c->erase_pending_list);
                        c->nr_erasing_blocks++;
                        jffs2_garbage_collect_trigger(c);
                } else {
                        /* Sometimes, however, we leave it elsewhere so it doesn't get
                           immediately reused, and we spread the load a bit. */
-                       D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
+                       jffs2_dbg(1, "...and adding to erasable_list\n");
                        list_add_tail(&jeb->list, &c->erasable_list);
                }
        }
@@ -136,7 +139,7 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
 
 static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
 {
-       D1(printk("About to refile bad block at %08x\n", jeb->offset));
+       jffs2_dbg(1, "About to refile bad block at %08x\n", jeb->offset);
 
        /* File the existing block on the bad_used_list.... */
        if (c->nextblock == jeb)
@@ -144,12 +147,14 @@ static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock
        else /* Not sure this should ever happen... need more coffee */
                list_del(&jeb->list);
        if (jeb->first_node) {
-               D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
+               jffs2_dbg(1, "Refiling block at %08x to bad_used_list\n",
+                         jeb->offset);
                list_add(&jeb->list, &c->bad_used_list);
        } else {
                BUG_ON(allow_empty == REFILE_NOTEMPTY);
                /* It has to have had some nodes or we couldn't be here */
-               D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
+               jffs2_dbg(1, "Refiling block at %08x to erase_pending_list\n",
+                         jeb->offset);
                list_add(&jeb->list, &c->erase_pending_list);
                c->nr_erasing_blocks++;
                jffs2_garbage_collect_trigger(c);
@@ -230,10 +235,12 @@ static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
 
        ret = mtd_read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify);
        if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
-               printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x failed: %d\n", c->wbuf_ofs, ret);
+               pr_warn("%s(): Read back of page at %08x failed: %d\n",
+                       __func__, c->wbuf_ofs, ret);
                return ret;
        } else if (retlen != c->wbuf_pagesize) {
-               printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x gave short read: %zd not %d.\n", ofs, retlen, c->wbuf_pagesize);
+               pr_warn("%s(): Read back of page at %08x gave short read: %zd not %d\n",
+                       __func__, ofs, retlen, c->wbuf_pagesize);
                return -EIO;
        }
        if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize))
@@ -246,12 +253,12 @@ static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
        else
                eccstr = "OK or unused";
 
-       printk(KERN_WARNING "Write verify error (ECC %s) at %08x. Wrote:\n",
-              eccstr, c->wbuf_ofs);
+       pr_warn("Write verify error (ECC %s) at %08x. Wrote:\n",
+               eccstr, c->wbuf_ofs);
        print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
                       c->wbuf, c->wbuf_pagesize, 0);
 
-       printk(KERN_WARNING "Read back:\n");
+       pr_warn("Read back:\n");
        print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
                       c->wbuf_verify, c->wbuf_pagesize, 0);
 
@@ -308,7 +315,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
 
        if (!first_raw) {
                /* All nodes were obsolete. Nothing to recover. */
-               D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n"));
+               jffs2_dbg(1, "No non-obsolete nodes to be recovered. Just filing block bad\n");
                c->wbuf_len = 0;
                return;
        }
@@ -331,7 +338,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
 
                buf = kmalloc(end - start, GFP_KERNEL);
                if (!buf) {
-                       printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
+                       pr_crit("Malloc failure in wbuf recovery. Data loss ensues.\n");
 
                        goto read_failed;
                }
@@ -346,7 +353,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
                        ret = 0;
 
                if (ret || retlen != c->wbuf_ofs - start) {
-                       printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
+                       pr_crit("Old data are already lost in wbuf recovery. Data loss ensues.\n");
 
                        kfree(buf);
                        buf = NULL;
@@ -380,7 +387,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
        /* ... and get an allocation of space from a shiny new block instead */
        ret = jffs2_reserve_space_gc(c, end-start, &len, JFFS2_SUMMARY_NOSUM_SIZE);
        if (ret) {
-               printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
+               pr_warn("Failed to allocate space for wbuf recovery. Data loss ensues.\n");
                kfree(buf);
                return;
        }
@@ -390,7 +397,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
 
        ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile);
        if (ret) {
-               printk(KERN_WARNING "Failed to allocate node refs for wbuf recovery. Data loss ensues.\n");
+               pr_warn("Failed to allocate node refs for wbuf recovery. Data loss ensues.\n");
                kfree(buf);
                return;
        }
@@ -406,13 +413,13 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
                unsigned char *rewrite_buf = buf?:c->wbuf;
                uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
 
-               D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
-                         towrite, ofs));
+               jffs2_dbg(1, "Write 0x%x bytes at 0x%08x in wbuf recover\n",
+                         towrite, ofs);
 
 #ifdef BREAKMEHEADER
                static int breakme;
                if (breakme++ == 20) {
-                       printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs);
+                       pr_notice("Faking write error at 0x%08x\n", ofs);
                        breakme = 0;
                        mtd_write(c->mtd, ofs, towrite, &retlen, brokenbuf);
                        ret = -EIO;
@@ -423,7 +430,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
 
                if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) {
                        /* Argh. We tried. Really we did. */
-                       printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
+                       pr_crit("Recovery of wbuf failed due to a second write error\n");
                        kfree(buf);
 
                        if (retlen)
@@ -431,7 +438,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
 
                        return;
                }
-               printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
+               pr_notice("Recovery of wbuf succeeded to %08x\n", ofs);
 
                c->wbuf_len = (end - start) - towrite;
                c->wbuf_ofs = ofs + towrite;
@@ -459,8 +466,8 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
                struct jffs2_raw_node_ref **adjust_ref = NULL;
                struct jffs2_inode_info *f = NULL;
 
-               D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
-                         rawlen, ref_offset(raw), ref_flags(raw), ofs));
+               jffs2_dbg(1, "Refiling block of %08x at %08x(%d) to %08x\n",
+                         rawlen, ref_offset(raw), ref_flags(raw), ofs);
 
                ic = jffs2_raw_ref_to_ic(raw);
 
@@ -540,7 +547,8 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
 
        /* Fix up the original jeb now it's on the bad_list */
        if (first_raw == jeb->first_node) {
-               D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
+               jffs2_dbg(1, "Failing block at %08x is now empty. Moving to erase_pending_list\n",
+                         jeb->offset);
                list_move(&jeb->list, &c->erase_pending_list);
                c->nr_erasing_blocks++;
                jffs2_garbage_collect_trigger(c);
@@ -554,7 +562,8 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
 
        spin_unlock(&c->erase_completion_lock);
 
-       D1(printk(KERN_DEBUG "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x\n", c->wbuf_ofs, c->wbuf_len));
+       jffs2_dbg(1, "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x\n",
+                 c->wbuf_ofs, c->wbuf_len);
 
 }
 
@@ -579,7 +588,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
                return 0;
 
        if (!mutex_is_locked(&c->alloc_sem)) {
-               printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
+               pr_crit("jffs2_flush_wbuf() called with alloc_sem not locked!\n");
                BUG();
        }
 
@@ -617,7 +626,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
 #ifdef BREAKME
        static int breakme;
        if (breakme++ == 20) {
-               printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs);
+               pr_notice("Faking write error at 0x%08x\n", c->wbuf_ofs);
                breakme = 0;
                mtd_write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen,
                          brokenbuf);
@@ -629,11 +638,11 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
                                &retlen, c->wbuf);
 
        if (ret) {
-               printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n", ret);
+               pr_warn("jffs2_flush_wbuf(): Write failed with %d\n", ret);
                goto wfail;
        } else if (retlen != c->wbuf_pagesize) {
-               printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
-                      retlen, c->wbuf_pagesize);
+               pr_warn("jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
+                       retlen, c->wbuf_pagesize);
                ret = -EIO;
                goto wfail;
        } else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) {
@@ -647,17 +656,18 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
        if (pad) {
                uint32_t waste = c->wbuf_pagesize - c->wbuf_len;
 
-               D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
-                         (wbuf_jeb==c->nextblock)?"next":"", wbuf_jeb->offset));
+               jffs2_dbg(1, "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
+                         (wbuf_jeb == c->nextblock) ? "next" : "",
+                         wbuf_jeb->offset);
 
                /* wbuf_pagesize - wbuf_len is the amount of space that's to be
                   padded. If there is less free space in the block than that,
                   something screwed up */
                if (wbuf_jeb->free_size < waste) {
-                       printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
-                              c->wbuf_ofs, c->wbuf_len, waste);
-                       printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
-                              wbuf_jeb->offset, wbuf_jeb->free_size);
+                       pr_crit("jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
+                               c->wbuf_ofs, c->wbuf_len, waste);
+                       pr_crit("jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
+                               wbuf_jeb->offset, wbuf_jeb->free_size);
                        BUG();
                }
 
@@ -694,14 +704,14 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
        uint32_t old_wbuf_len;
        int ret = 0;
 
-       D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
+       jffs2_dbg(1, "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino);
 
        if (!c->wbuf)
                return 0;
 
        mutex_lock(&c->alloc_sem);
        if (!jffs2_wbuf_pending_for_ino(c, ino)) {
-               D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
+               jffs2_dbg(1, "Ino #%d not pending in wbuf. Returning\n", ino);
                mutex_unlock(&c->alloc_sem);
                return 0;
        }
@@ -711,7 +721,8 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
 
        if (c->unchecked_size) {
                /* GC won't make any progress for a while */
-               D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
+               jffs2_dbg(1, "%s(): padding. Not finished checking\n",
+                         __func__);
                down_write(&c->wbuf_sem);
                ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
                /* retry flushing wbuf in case jffs2_wbuf_recover
@@ -724,7 +735,7 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
 
                mutex_unlock(&c->alloc_sem);
 
-               D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
+               jffs2_dbg(1, "%s(): calls gc pass\n", __func__);
 
                ret = jffs2_garbage_collect_pass(c);
                if (ret) {
@@ -742,7 +753,7 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
                mutex_lock(&c->alloc_sem);
        }
 
-       D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
+       jffs2_dbg(1, "%s(): ends...\n", __func__);
 
        mutex_unlock(&c->alloc_sem);
        return ret;
@@ -811,9 +822,8 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs,
        if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
                /* It's a write to a new block */
                if (c->wbuf_len) {
-                       D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx "
-                                 "causes flush of wbuf at 0x%08x\n",
-                                 (unsigned long)to, c->wbuf_ofs));
+                       jffs2_dbg(1, "%s(): to 0x%lx causes flush of wbuf at 0x%08x\n",
+                                 __func__, (unsigned long)to, c->wbuf_ofs);
                        ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
                        if (ret)
                                goto outerr;
@@ -825,11 +835,11 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs,
 
        if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
                /* We're not writing immediately after the writebuffer. Bad. */
-               printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write "
-                      "to %08lx\n", (unsigned long)to);
+               pr_crit("%s(): Non-contiguous write to %08lx\n",
+                       __func__, (unsigned long)to);
                if (c->wbuf_len)
-                       printk(KERN_CRIT "wbuf was previously %08x-%08x\n",
-                              c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len);
+                       pr_crit("wbuf was previously %08x-%08x\n",
+                               c->wbuf_ofs, c->wbuf_ofs + c->wbuf_len);
                BUG();
        }
 
@@ -957,8 +967,8 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
 
        if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) {
                if (ret == -EBADMSG)
-                       printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx)"
-                              " returned ECC error\n", len, ofs);
+                       pr_warn("mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
+                               len, ofs);
                /*
                 * We have the raw data without ECC correction in the buffer,
                 * maybe we are lucky and all data or parts are correct. We
@@ -1034,9 +1044,8 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
 
        ret = mtd_read_oob(c->mtd, jeb->offset, &ops);
        if (ret || ops.oobretlen != ops.ooblen) {
-               printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
-                               " bytes, read %zd bytes, error %d\n",
-                               jeb->offset, ops.ooblen, ops.oobretlen, ret);
+               pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n",
+                      jeb->offset, ops.ooblen, ops.oobretlen, ret);
                if (!ret)
                        ret = -EIO;
                return ret;
@@ -1048,8 +1057,8 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
                        continue;
 
                if (ops.oobbuf[i] != 0xFF) {
-                       D2(printk(KERN_DEBUG "Found %02x at %x in OOB for "
-                                 "%08x\n", ops.oobbuf[i], i, jeb->offset));
+                       jffs2_dbg(2, "Found %02x at %x in OOB for "
+                                 "%08x\n", ops.oobbuf[i], i, jeb->offset);
                        return 1;
                }
        }
@@ -1077,9 +1086,8 @@ int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
 
        ret = mtd_read_oob(c->mtd, jeb->offset, &ops);
        if (ret || ops.oobretlen != ops.ooblen) {
-               printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
-                               " bytes, read %zd bytes, error %d\n",
-                               jeb->offset, ops.ooblen, ops.oobretlen, ret);
+               pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n",
+                      jeb->offset, ops.ooblen, ops.oobretlen, ret);
                if (!ret)
                        ret = -EIO;
                return ret;
@@ -1103,9 +1111,8 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
 
        ret = mtd_write_oob(c->mtd, jeb->offset, &ops);
        if (ret || ops.oobretlen != ops.ooblen) {
-               printk(KERN_ERR "cannot write OOB for EB at %08x, requested %zd"
-                               " bytes, read %zd bytes, error %d\n",
-                               jeb->offset, ops.ooblen, ops.oobretlen, ret);
+               pr_err("cannot write OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n",
+                      jeb->offset, ops.ooblen, ops.oobretlen, ret);
                if (!ret)
                        ret = -EIO;
                return ret;
@@ -1130,11 +1137,12 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *
        if( ++jeb->bad_count < MAX_ERASE_FAILURES)
                return 0;
 
-       printk(KERN_WARNING "JFFS2: marking eraseblock at %08x\n as bad", bad_offset);
+       pr_warn("marking eraseblock at %08x as bad\n", bad_offset);
        ret = mtd_block_markbad(c->mtd, bad_offset);
 
        if (ret) {
-               D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
+               jffs2_dbg(1, "%s(): Write failed for block at %08x: error %d\n",
+                         __func__, jeb->offset, ret);
                return ret;
        }
        return 1;
@@ -1151,11 +1159,11 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
        c->cleanmarker_size = 0;
 
        if (!oinfo || oinfo->oobavail == 0) {
-               printk(KERN_ERR "inconsistent device description\n");
+               pr_err("inconsistent device description\n");
                return -EINVAL;
        }
 
-       D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n"));
+       jffs2_dbg(1, "using OOB on NAND\n");
 
        c->oobavail = oinfo->oobavail;
 
@@ -1222,7 +1230,7 @@ int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
 
        if ((c->flash_size % c->sector_size) != 0) {
                c->flash_size = (c->flash_size / c->sector_size) * c->sector_size;
-               printk(KERN_WARNING "JFFS2 flash size adjusted to %dKiB\n", c->flash_size);
+               pr_warn("flash size adjusted to %dKiB\n", c->flash_size);
        };
 
        c->wbuf_ofs = 0xFFFFFFFF;
@@ -1239,7 +1247,8 @@ int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
        }
 #endif
 
-       printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size);
+       pr_info("write-buffering enabled buffer (%d) erasesize (%d)\n",
+               c->wbuf_pagesize, c->sector_size);
 
        return 0;
 }
@@ -1297,7 +1306,8 @@ int jffs2_ubivol_setup(struct jffs2_sb_info *c) {
        if (!c->wbuf)
                return -ENOMEM;
 
-       printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size);
+       pr_info("write-buffering enabled buffer (%d) erasesize (%d)\n",
+               c->wbuf_pagesize, c->sector_size);
 
        return 0;
 }
index 30d175b6d290698fad6a38f6bbafba755eee90b4..b634de4c81013eec049194c803226d724a3e02fc 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/crc32.h>
@@ -36,7 +38,7 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
        f->inocache->state = INO_STATE_PRESENT;
 
        jffs2_add_ino_cache(c, f->inocache);
-       D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
+       jffs2_dbg(1, "%s(): Assigned ino# %d\n", __func__, f->inocache->ino);
        ri->ino = cpu_to_je32(f->inocache->ino);
 
        ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -68,7 +70,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
        unsigned long cnt = 2;
 
        D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
-               printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
+               pr_crit("Eep. CRC not correct in jffs2_write_dnode()\n");
                BUG();
        }
           );
@@ -78,7 +80,9 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
        vecs[1].iov_len = datalen;
 
        if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
-               printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
+               pr_warn("%s(): ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n",
+                       __func__, je32_to_cpu(ri->totlen),
+                       sizeof(*ri), datalen);
        }
 
        fn = jffs2_alloc_full_dnode();
@@ -95,9 +99,9 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
 
        if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
                BUG_ON(!retried);
-               D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
-                               "highest version %d -> updating dnode\n",
-                               je32_to_cpu(ri->version), f->highest_version));
+               jffs2_dbg(1, "%s(): dnode_version %d, highest version %d -> updating dnode\n",
+                         __func__,
+                         je32_to_cpu(ri->version), f->highest_version);
                ri->version = cpu_to_je32(++f->highest_version);
                ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
        }
@@ -106,8 +110,8 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
                                 (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
 
        if (ret || (retlen != sizeof(*ri) + datalen)) {
-               printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
-                      sizeof(*ri)+datalen, flash_ofs, ret, retlen);
+               pr_notice("Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
+                         sizeof(*ri) + datalen, flash_ofs, ret, retlen);
 
                /* Mark the space as dirtied */
                if (retlen) {
@@ -118,7 +122,8 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
                           this node */
                        jffs2_add_physical_node_ref(c, flash_ofs | REF_OBSOLETE, PAD(sizeof(*ri)+datalen), NULL);
                } else {
-                       printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", flash_ofs);
+                       pr_notice("Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n",
+                                 flash_ofs);
                }
                if (!retried && alloc_mode != ALLOC_NORETRY) {
                        /* Try to reallocate space and retry */
@@ -127,7 +132,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
 
                        retried = 1;
 
-                       D1(printk(KERN_DEBUG "Retrying failed write.\n"));
+                       jffs2_dbg(1, "Retrying failed write.\n");
 
                        jffs2_dbg_acct_sanity_check(c,jeb);
                        jffs2_dbg_acct_paranoia_check(c, jeb);
@@ -147,14 +152,16 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
 
                        if (!ret) {
                                flash_ofs = write_ofs(c);
-                               D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
+                               jffs2_dbg(1, "Allocated space at 0x%08x to retry failed write.\n",
+                                         flash_ofs);
 
                                jffs2_dbg_acct_sanity_check(c,jeb);
                                jffs2_dbg_acct_paranoia_check(c, jeb);
 
                                goto retry;
                        }
-                       D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+                       jffs2_dbg(1, "Failed to allocate space to retry failed write: %d!\n",
+                                 ret);
                }
                /* Release the full_dnode which is now useless, and return */
                jffs2_free_full_dnode(fn);
@@ -183,10 +190,10 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
        fn->size = je32_to_cpu(ri->dsize);
        fn->frags = 0;
 
-       D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
+       jffs2_dbg(1, "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
                  flash_ofs & ~3, flash_ofs & 3, je32_to_cpu(ri->dsize),
                  je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
-                 je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
+                 je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen));
 
        if (retried) {
                jffs2_dbg_acct_sanity_check(c,NULL);
@@ -206,22 +213,23 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
        int retried = 0;
        int ret;
 
-       D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n",
+       jffs2_dbg(1, "%s(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n",
+                 __func__,
                  je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
-                 je32_to_cpu(rd->name_crc)));
+                 je32_to_cpu(rd->name_crc));
 
        D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
-               printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
+               pr_crit("Eep. CRC not correct in jffs2_write_dirent()\n");
                BUG();
           });
 
        if (strnlen(name, namelen) != namelen) {
                /* This should never happen, but seems to have done on at least one
                   occasion: https://dev.laptop.org/ticket/4184 */
-               printk(KERN_CRIT "Error in jffs2_write_dirent() -- name contains zero bytes!\n");
-               printk(KERN_CRIT "Directory inode #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x\n",
-                      je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
-                      je32_to_cpu(rd->name_crc));
+               pr_crit("Error in jffs2_write_dirent() -- name contains zero bytes!\n");
+               pr_crit("Directory inode #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x\n",
+                       je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
+                       je32_to_cpu(rd->name_crc));
                WARN_ON(1);
                return ERR_PTR(-EIO);
        }
@@ -249,9 +257,9 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
 
        if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
                BUG_ON(!retried);
-               D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
-                                    "highest version %d -> updating dirent\n",
-                                    je32_to_cpu(rd->version), f->highest_version));
+               jffs2_dbg(1, "%s(): dirent_version %d, highest version %d -> updating dirent\n",
+                         __func__,
+                         je32_to_cpu(rd->version), f->highest_version);
                rd->version = cpu_to_je32(++f->highest_version);
                fd->version = je32_to_cpu(rd->version);
                rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
@@ -260,13 +268,14 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
        ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
                                 (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
        if (ret || (retlen != sizeof(*rd) + namelen)) {
-               printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
-                              sizeof(*rd)+namelen, flash_ofs, ret, retlen);
+               pr_notice("Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
+                         sizeof(*rd) + namelen, flash_ofs, ret, retlen);
                /* Mark the space as dirtied */
                if (retlen) {
                        jffs2_add_physical_node_ref(c, flash_ofs | REF_OBSOLETE, PAD(sizeof(*rd)+namelen), NULL);
                } else {
-                       printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", flash_ofs);
+                       pr_notice("Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n",
+                                 flash_ofs);
                }
                if (!retried) {
                        /* Try to reallocate space and retry */
@@ -275,7 +284,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
 
                        retried = 1;
 
-                       D1(printk(KERN_DEBUG "Retrying failed write.\n"));
+                       jffs2_dbg(1, "Retrying failed write.\n");
 
                        jffs2_dbg_acct_sanity_check(c,jeb);
                        jffs2_dbg_acct_paranoia_check(c, jeb);
@@ -295,12 +304,14 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
 
                        if (!ret) {
                                flash_ofs = write_ofs(c);
-                               D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
+                               jffs2_dbg(1, "Allocated space at 0x%08x to retry failed write\n",
+                                         flash_ofs);
                                jffs2_dbg_acct_sanity_check(c,jeb);
                                jffs2_dbg_acct_paranoia_check(c, jeb);
                                goto retry;
                        }
-                       D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+                       jffs2_dbg(1, "Failed to allocate space to retry failed write: %d!\n",
+                                 ret);
                }
                /* Release the full_dnode which is now useless, and return */
                jffs2_free_full_dirent(fd);
@@ -333,8 +344,8 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
        int ret = 0;
        uint32_t writtenlen = 0;
 
-               D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n",
-                 f->inocache->ino, offset, writelen));
+       jffs2_dbg(1, "%s(): Ino #%u, ofs 0x%x, len 0x%x\n",
+                 __func__, f->inocache->ino, offset, writelen);
 
        while(writelen) {
                struct jffs2_full_dnode *fn;
@@ -345,12 +356,13 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                int retried = 0;
 
        retry:
-               D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
+               jffs2_dbg(2, "jffs2_commit_write() loop: 0x%x to write to 0x%x\n",
+                         writelen, offset);
 
                ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN,
                                        &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
                if (ret) {
-                       D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
+                       jffs2_dbg(1, "jffs2_reserve_space returned %d\n", ret);
                        break;
                }
                mutex_lock(&f->sem);
@@ -386,7 +398,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                        if (!retried) {
                                /* Write error to be retried */
                                retried = 1;
-                               D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n"));
+                               jffs2_dbg(1, "Retrying node write in jffs2_write_inode_range()\n");
                                goto retry;
                        }
                        break;
@@ -399,7 +411,8 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                }
                if (ret) {
                        /* Eep */
-                       D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
+                       jffs2_dbg(1, "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n",
+                                 ret);
                        jffs2_mark_node_obsolete(c, fn->raw);
                        jffs2_free_full_dnode(fn);
 
@@ -410,11 +423,11 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                mutex_unlock(&f->sem);
                jffs2_complete_reservation(c);
                if (!datalen) {
-                       printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
+                       pr_warn("Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
                        ret = -EIO;
                        break;
                }
-               D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
+               jffs2_dbg(1, "increasing writtenlen by %d\n", datalen);
                writtenlen += datalen;
                offset += datalen;
                writelen -= datalen;
@@ -439,7 +452,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
         */
        ret = jffs2_reserve_space(c, sizeof(*ri), &alloclen, ALLOC_NORMAL,
                                JFFS2_SUMMARY_INODE_SIZE);
-       D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
+       jffs2_dbg(1, "%s(): reserved 0x%x bytes\n", __func__, alloclen);
        if (ret)
                return ret;
 
@@ -450,11 +463,11 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
 
        fn = jffs2_write_dnode(c, f, ri, NULL, 0, ALLOC_NORMAL);
 
-       D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
-                 jemode_to_cpu(ri->mode)));
+       jffs2_dbg(1, "jffs2_do_create created file with mode 0x%x\n",
+                 jemode_to_cpu(ri->mode));
 
        if (IS_ERR(fn)) {
-               D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
+               jffs2_dbg(1, "jffs2_write_dnode() failed\n");
                /* Eeek. Wave bye bye */
                mutex_unlock(&f->sem);
                jffs2_complete_reservation(c);
@@ -480,7 +493,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
 
        if (ret) {
                /* Eep. */
-               D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
+               jffs2_dbg(1, "jffs2_reserve_space() for dirent failed\n");
                return ret;
        }
 
@@ -597,8 +610,8 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
                            !memcmp(fd->name, name, namelen) &&
                            !fd->name[namelen]) {
 
-                               D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
-                                         fd->ino, ref_offset(fd->raw)));
+                               jffs2_dbg(1, "Marking old dirent node (ino #%u) @%08x obsolete\n",
+                                         fd->ino, ref_offset(fd->raw));
                                jffs2_mark_node_obsolete(c, fd->raw);
                                /* We don't want to remove it from the list immediately,
                                   because that screws up getdents()/seek() semantics even
@@ -627,11 +640,13 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
                                dead_f->dents = fd->next;
 
                                if (fd->ino) {
-                                       printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
-                                              dead_f->inocache->ino, fd->name, fd->ino);
+                                       pr_warn("Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
+                                               dead_f->inocache->ino,
+                                               fd->name, fd->ino);
                                } else {
-                                       D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n",
-                                               fd->name, dead_f->inocache->ino));
+                                       jffs2_dbg(1, "Removing deletion dirent for \"%s\" from dir ino #%u\n",
+                                                 fd->name,
+                                                 dead_f->inocache->ino);
                                }
                                if (fd->raw)
                                        jffs2_mark_node_obsolete(c, fd->raw);
index 3e93cdd1900552d18e900f3b255eff3d356c6a48..b55b803eddcb92081908aa1f53da50cbdfc6babb 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
index 71e2b4d50a0ab5104087237c350c20057dc3e38f..f86f51f99acebb05df6b2c404b7186e2b6d1194c 100644 (file)
@@ -19,7 +19,7 @@
 #endif
 
 #ifdef CONFIG_ROMFS_ON_MTD
-#define ROMFS_MTD_READ(sb, ...) ((sb)->s_mtd->read((sb)->s_mtd, ##__VA_ARGS__))
+#define ROMFS_MTD_READ(sb, ...) mtd_read((sb)->s_mtd, ##__VA_ARGS__)
 
 /*
  * read data from an romfs image on an MTD device
similarity index 92%
rename from drivers/acpi/acpica/acconfig.h
rename to include/acpi/acconfig.h
index 1f30af613e87619d4ab7cfeec3d45dad1d15d386..03f14856bd09f66d7a9a9b48074da7bbbfe4042f 100644 (file)
  */
 #define ACPI_CHECKSUM_ABORT             FALSE
 
+/*
+ * Generate a version of ACPICA that only supports "reduced hardware"
+ * platforms (as defined in ACPI 5.0). Set to TRUE to generate a specialized
+ * version of ACPICA that ONLY supports the ACPI 5.0 "reduced hardware"
+ * model. In other words, no ACPI hardware is supported.
+ *
+ * If TRUE, this means no support for the following:
+ *      PM Event and Control registers
+ *      SCI interrupt (and handler)
+ *      Fixed Events
+ *      General Purpose Events (GPEs)
+ *      Global Lock
+ *      ACPI PM timer
+ *      FACS table (Waking vectors and Global Lock)
+ */
+#define ACPI_REDUCED_HARDWARE           FALSE
+
 /******************************************************************************
  *
  * Subsystem Constants
 
 /* Version of ACPI supported */
 
-#define ACPI_CA_SUPPORT_LEVEL           3
+#define ACPI_CA_SUPPORT_LEVEL           5
 
 /* Maximum count for a semaphore object */
 
index 5b6c391efc8e658adba3eac683f46ea3938495ab..92d6e1d701ff13466a5c8c5361b4beed017ba953 100644 (file)
@@ -57,6 +57,7 @@
 #define ACPI_SUCCESS(a)                 (!(a))
 #define ACPI_FAILURE(a)                 (a)
 
+#define ACPI_SKIP(a)                    (a == AE_CTRL_SKIP)
 #define AE_OK                           (acpi_status) 0x0000
 
 /*
@@ -89,8 +90,9 @@
 #define AE_SAME_HANDLER                 (acpi_status) (0x0019 | AE_CODE_ENVIRONMENTAL)
 #define AE_NO_HANDLER                   (acpi_status) (0x001A | AE_CODE_ENVIRONMENTAL)
 #define AE_OWNER_ID_LIMIT               (acpi_status) (0x001B | AE_CODE_ENVIRONMENTAL)
+#define AE_NOT_CONFIGURED               (acpi_status) (0x001C | AE_CODE_ENVIRONMENTAL)
 
-#define AE_CODE_ENV_MAX                 0x001B
+#define AE_CODE_ENV_MAX                 0x001C
 
 /*
  * Programmer exceptions
@@ -213,7 +215,8 @@ char const *acpi_gbl_exception_names_env[] = {
        "AE_ABORT_METHOD",
        "AE_SAME_HANDLER",
        "AE_NO_HANDLER",
-       "AE_OWNER_ID_LIMIT"
+       "AE_OWNER_ID_LIMIT",
+       "AE_NOT_CONFIGURED"
 };
 
 char const *acpi_gbl_exception_names_pgm[] = {
index 5b5af0d30a9738a7cb938a04b839c3d6a5f5727e..38f508816e4a7a2e9f3c5133de7a662a649141a1 100644 (file)
@@ -46,6 +46,7 @@
 
 /* Method names - these methods can appear anywhere in the namespace */
 
+#define METHOD_NAME__SB_        "_SB_"
 #define METHOD_NAME__HID        "_HID"
 #define METHOD_NAME__CID        "_CID"
 #define METHOD_NAME__UID        "_UID"
 
 /* Method names - these methods must appear at the namespace root */
 
-#define METHOD_NAME__BFS        "\\_BFS"
-#define METHOD_NAME__GTS        "\\_GTS"
-#define METHOD_NAME__PTS        "\\_PTS"
-#define METHOD_NAME__SST        "\\_SI._SST"
-#define METHOD_NAME__WAK        "\\_WAK"
+#define METHOD_PATHNAME__BFS    "\\_BFS"
+#define METHOD_PATHNAME__GTS    "\\_GTS"
+#define METHOD_PATHNAME__PTS    "\\_PTS"
+#define METHOD_PATHNAME__SST    "\\_SI._SST"
+#define METHOD_PATHNAME__WAK    "\\_WAK"
 
 /* Definitions of the predefined namespace names  */
 
@@ -79,6 +80,5 @@
 #define ACPI_PREFIX_LOWER       (u32) 0x69706361       /* "acpi" */
 
 #define ACPI_NS_ROOT_PATH       "\\"
-#define ACPI_NS_SYSTEM_BUS      "_SB_"
 
 #endif                         /* __ACNAMES_H__  */
index 6cd5b6403a7b9f37eac4317807c97003c79cd9ba..f1c8ca60e8242b610b036074c459094a22d533f1 100644 (file)
@@ -323,6 +323,8 @@ int acpi_bus_set_power(acpi_handle handle, int state);
 int acpi_bus_update_power(acpi_handle handle, int *state_p);
 bool acpi_bus_power_manageable(acpi_handle handle);
 bool acpi_bus_can_wakeup(acpi_handle handle);
+int acpi_power_resource_register_device(struct device *dev, acpi_handle handle);
+void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle);
 #ifdef CONFIG_ACPI_PROC_EVENT
 int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data);
 int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data);
@@ -392,8 +394,13 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p)
 #endif
 
 #ifdef CONFIG_PM_SLEEP
+int acpi_pm_device_run_wake(struct device *, bool);
 int acpi_pm_device_sleep_wake(struct device *, bool);
 #else
+static inline int acpi_pm_device_run_wake(struct device *dev, bool enable)
+{
+       return -ENODEV;
+}
 static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
 {
        return -ENODEV;
index 7c9aebe8a7aa27ebf091c1a0ec2470e1e23d7f0a..21a5548c66865cb2b3a79d18807b79ce771f6d9f 100644 (file)
@@ -95,6 +95,11 @@ acpi_status
 acpi_os_table_override(struct acpi_table_header *existing_table,
                       struct acpi_table_header **new_table);
 
+acpi_status
+acpi_os_physical_table_override(struct acpi_table_header *existing_table,
+                               acpi_physical_address * new_address,
+                               u32 *new_table_length);
+
 /*
  * Spinlock primitives
  */
@@ -217,14 +222,10 @@ acpi_status acpi_os_write_port(acpi_io_address address, u32 value, u32 width);
  * Platform and hardware-independent physical memory interfaces
  */
 acpi_status
-acpi_os_read_memory(acpi_physical_address address, u32 * value, u32 width);
-acpi_status
-acpi_os_read_memory64(acpi_physical_address address, u64 *value, u32 width);
+acpi_os_read_memory(acpi_physical_address address, u64 *value, u32 width);
 
 acpi_status
-acpi_os_write_memory(acpi_physical_address address, u32 value, u32 width);
-acpi_status
-acpi_os_write_memory64(acpi_physical_address address, u64 value, u32 width);
+acpi_os_write_memory(acpi_physical_address address, u64 value, u32 width);
 
 /*
  * Platform and hardware-independent PCI configuration space access
index a28da35ba45ee3f1fd3bcdf2b8a7f64bbe5d2391..9821101346726a27b54eec467a65012de45de466 100644 (file)
@@ -47,8 +47,9 @@
 
 /* Current ACPICA subsystem version in YYYYMMDD format */
 
-#define ACPI_CA_VERSION                 0x20120111
+#define ACPI_CA_VERSION                 0x20120320
 
+#include "acconfig.h"
 #include "actypes.h"
 #include "actbl.h"
 
@@ -71,6 +72,33 @@ extern u8 acpi_gbl_copy_dsdt_locally;
 extern u8 acpi_gbl_truncate_io_addresses;
 extern u8 acpi_gbl_disable_auto_repair;
 
+/*
+ * Hardware-reduced prototypes. All interfaces that use these macros will
+ * be configured out of the ACPICA build if the ACPI_REDUCED_HARDWARE flag
+ * is set to TRUE.
+ */
+#if (!ACPI_REDUCED_HARDWARE)
+#define ACPI_HW_DEPENDENT_RETURN_STATUS(prototype) \
+       prototype;
+
+#define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
+       prototype;
+
+#define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
+       prototype;
+
+#else
+#define ACPI_HW_DEPENDENT_RETURN_STATUS(prototype) \
+       static ACPI_INLINE prototype {return(AE_NOT_CONFIGURED);}
+
+#define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
+       static ACPI_INLINE prototype {return(AE_OK);}
+
+#define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
+       static ACPI_INLINE prototype {}
+
+#endif                         /* !ACPI_REDUCED_HARDWARE */
+
 extern u32 acpi_current_gpe_count;
 extern struct acpi_table_fadt acpi_gbl_FADT;
 extern u8 acpi_gbl_system_awake_and_running;
@@ -96,9 +124,8 @@ acpi_status acpi_terminate(void);
 acpi_status acpi_subsystem_status(void);
 #endif
 
-acpi_status acpi_enable(void);
-
-acpi_status acpi_disable(void);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable(void))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable(void))
 
 #ifdef ACPI_FUTURE_USAGE
 acpi_status acpi_get_system_info(struct acpi_buffer *ret_buffer);
@@ -235,17 +262,34 @@ acpi_status acpi_get_parent(acpi_handle object, acpi_handle * out_handle);
 acpi_status
 acpi_install_initialization_handler(acpi_init_handler handler, u32 function);
 
-acpi_status
-acpi_install_global_event_handler(ACPI_GBL_EVENT_HANDLER handler,
-                                void *context);
-
-acpi_status
-acpi_install_fixed_event_handler(u32 acpi_event,
-                                acpi_event_handler handler, void *context);
-
-acpi_status
-acpi_remove_fixed_event_handler(u32 acpi_event, acpi_event_handler handler);
-
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_install_global_event_handler
+                               (ACPI_GBL_EVENT_HANDLER handler, void *context))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                                acpi_install_fixed_event_handler(u32
+                                                                 acpi_event,
+                                                                 acpi_event_handler
+                                                                 handler,
+                                                                 void
+                                                                 *context))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                                acpi_remove_fixed_event_handler(u32 acpi_event,
+                                                                acpi_event_handler
+                                                                handler))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                                acpi_install_gpe_handler(acpi_handle
+                                                         gpe_device,
+                                                         u32 gpe_number,
+                                                         u32 type,
+                                                         acpi_gpe_handler
+                                                         address,
+                                                         void *context))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                                acpi_remove_gpe_handler(acpi_handle gpe_device,
+                                                        u32 gpe_number,
+                                                        acpi_gpe_handler
+                                                        address))
 acpi_status
 acpi_install_notify_handler(acpi_handle device,
                            u32 handler_type,
@@ -266,15 +310,6 @@ acpi_remove_address_space_handler(acpi_handle device,
                                  acpi_adr_space_type space_id,
                                  acpi_adr_space_handler handler);
 
-acpi_status
-acpi_install_gpe_handler(acpi_handle gpe_device,
-                        u32 gpe_number,
-                        u32 type, acpi_gpe_handler address, void *context);
-
-acpi_status
-acpi_remove_gpe_handler(acpi_handle gpe_device,
-                       u32 gpe_number, acpi_gpe_handler address);
-
 #ifdef ACPI_FUTURE_USAGE
 acpi_status acpi_install_exception_handler(acpi_exception_handler handler);
 #endif
@@ -284,9 +319,11 @@ acpi_status acpi_install_interface_handler(acpi_interface_handler handler);
 /*
  * Global Lock interfaces
  */
-acpi_status acpi_acquire_global_lock(u16 timeout, u32 * handle);
-
-acpi_status acpi_release_global_lock(u32 handle);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_acquire_global_lock(u16 timeout,
+                                                        u32 *handle))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_release_global_lock(u32 handle))
 
 /*
  * Interfaces to AML mutex objects
@@ -299,47 +336,75 @@ acpi_status acpi_release_mutex(acpi_handle handle, acpi_string pathname);
 /*
  * Fixed Event interfaces
  */
-acpi_status acpi_enable_event(u32 event, u32 flags);
-
-acpi_status acpi_disable_event(u32 event, u32 flags);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_enable_event(u32 event, u32 flags))
 
-acpi_status acpi_clear_event(u32 event);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_disable_event(u32 event, u32 flags))
 
-acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_clear_event(u32 event))
 
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_get_event_status(u32 event,
+                                                     acpi_event_status
+                                                     *event_status))
 /*
  * General Purpose Event (GPE) Interfaces
  */
-acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number);
-
-acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number);
-
-acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number);
-
-acpi_status
-acpi_setup_gpe_for_wake(acpi_handle parent_device,
-                       acpi_handle gpe_device, u32 gpe_number);
-
-acpi_status acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 action);
-
-acpi_status
-acpi_get_gpe_status(acpi_handle gpe_device,
-                   u32 gpe_number, acpi_event_status *event_status);
-
-acpi_status acpi_disable_all_gpes(void);
-
-acpi_status acpi_enable_all_runtime_gpes(void);
-
-acpi_status acpi_get_gpe_device(u32 gpe_index, acpi_handle *gpe_device);
-
-acpi_status
-acpi_install_gpe_block(acpi_handle gpe_device,
-                      struct acpi_generic_address *gpe_block_address,
-                      u32 register_count, u32 interrupt_number);
-
-acpi_status acpi_remove_gpe_block(acpi_handle gpe_device);
-
-acpi_status acpi_update_all_gpes(void);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_update_all_gpes(void))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_enable_gpe(acpi_handle gpe_device,
+                                               u32 gpe_number))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_disable_gpe(acpi_handle gpe_device,
+                                                u32 gpe_number))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_clear_gpe(acpi_handle gpe_device,
+                                              u32 gpe_number))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_set_gpe(acpi_handle gpe_device,
+                                            u32 gpe_number, u8 action))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_finish_gpe(acpi_handle gpe_device,
+                                               u32 gpe_number))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_setup_gpe_for_wake(acpi_handle
+                                                       parent_device,
+                                                       acpi_handle gpe_device,
+                                                       u32 gpe_number))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                                acpi_set_gpe_wake_mask(acpi_handle gpe_device,
+                                                       u32 gpe_number,
+                                                       u8 action))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                                acpi_get_gpe_status(acpi_handle gpe_device,
+                                                    u32 gpe_number,
+                                                    acpi_event_status
+                                                    *event_status))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_get_gpe_device(u32 gpe_index,
+                                                   acpi_handle * gpe_device))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_install_gpe_block(acpi_handle gpe_device,
+                                                      struct
+                                                      acpi_generic_address
+                                                      *gpe_block_address,
+                                                      u32 register_count,
+                                                      u32 interrupt_number))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                                acpi_remove_gpe_block(acpi_handle gpe_device))
 
 /*
  * Resource interfaces
@@ -391,33 +456,59 @@ acpi_buffer_to_resource(u8 *aml_buffer,
  */
 acpi_status acpi_reset(void);
 
-acpi_status acpi_read_bit_register(u32 register_id, u32 *return_value);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_read_bit_register(u32 register_id,
+                                                      u32 *return_value))
 
-acpi_status acpi_write_bit_register(u32 register_id, u32 value);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_write_bit_register(u32 register_id,
+                                                       u32 value))
 
-acpi_status acpi_set_firmware_waking_vector(u32 physical_address);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_set_firmware_waking_vector(u32
+                                                               physical_address))
 
 #if ACPI_MACHINE_WIDTH == 64
-acpi_status acpi_set_firmware_waking_vector64(u64 physical_address);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_set_firmware_waking_vector64(u64
+                                                                 physical_address))
 #endif
 
 acpi_status acpi_read(u64 *value, struct acpi_generic_address *reg);
 
 acpi_status acpi_write(u64 value, struct acpi_generic_address *reg);
 
+/*
+ * Sleep/Wake interfaces
+ */
 acpi_status
 acpi_get_sleep_type_data(u8 sleep_state, u8 * slp_typ_a, u8 * slp_typ_b);
 
 acpi_status acpi_enter_sleep_state_prep(u8 sleep_state);
 
-acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state);
+acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags);
 
-acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void))
 
-acpi_status acpi_leave_sleep_state_prep(u8 sleep_state);
+acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags);
 
 acpi_status acpi_leave_sleep_state(u8 sleep_state);
 
+/*
+ * ACPI Timer interfaces
+ */
+#ifdef ACPI_FUTURE_USAGE
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_get_timer_resolution(u32 *resolution))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_get_timer(u32 *ticks))
+
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                               acpi_get_timer_duration(u32 start_ticks,
+                                                       u32 end_ticks,
+                                                       u32 *time_elapsed))
+#endif                         /* ACPI_FUTURE_USAGE */
+
 /*
  * Error/Warning output
  */
index 8e1b92f6f650332e6130486d6b7e7a5bd3f1b3f8..8dea54665dcf35b4c0bfdcd63b6920881d32e699 100644 (file)
@@ -309,6 +309,13 @@ enum acpi_prefered_pm_profiles {
        PM_TABLET = 8
 };
 
+/* Values for sleep_status and sleep_control registers (V5 FADT) */
+
+#define ACPI_X_WAKE_STATUS          0x80
+#define ACPI_X_SLEEP_TYPE_MASK      0x1C
+#define ACPI_X_SLEEP_TYPE_POSITION  0x02
+#define ACPI_X_SLEEP_ENABLE         0x20
+
 /* Reset to default packing */
 
 #pragma pack()
index d5dee7ce9474ee88c4bf3cda01339e74067311ad..eba66043cf1b7d1dccfc1956446ea9bafdbb588c 100644 (file)
@@ -517,6 +517,13 @@ typedef u64 acpi_integer;
 #define ACPI_SLEEP_TYPE_MAX             0x7
 #define ACPI_SLEEP_TYPE_INVALID         0xFF
 
+/*
+ * Sleep/Wake flags
+ */
+#define ACPI_NO_OPTIONAL_METHODS        0x00   /* Do not execute any optional methods */
+#define ACPI_EXECUTE_GTS                0x01   /* For enter sleep interface */
+#define ACPI_EXECUTE_BFS                0x02   /* For leave sleep prep interface */
+
 /*
  * Standard notify values
  */
@@ -532,8 +539,9 @@ typedef u64 acpi_integer;
 #define ACPI_NOTIFY_DEVICE_PLD_CHECK    (u8) 0x09
 #define ACPI_NOTIFY_RESERVED            (u8) 0x0A
 #define ACPI_NOTIFY_LOCALITY_UPDATE     (u8) 0x0B
+#define ACPI_NOTIFY_SHUTDOWN_REQUEST    (u8) 0x0C
 
-#define ACPI_NOTIFY_MAX                 0x0B
+#define ACPI_NOTIFY_MAX                 0x0C
 
 /*
  * Types associated with ACPI names and objects. The first group of
@@ -698,7 +706,8 @@ typedef u32 acpi_event_status;
 #define ACPI_ALL_NOTIFY                 (ACPI_SYSTEM_NOTIFY | ACPI_DEVICE_NOTIFY)
 #define ACPI_MAX_NOTIFY_HANDLER_TYPE    0x3
 
-#define ACPI_MAX_SYS_NOTIFY             0x7f
+#define ACPI_MAX_SYS_NOTIFY             0x7F
+#define ACPI_MAX_DEVICE_SPECIFIC_NOTIFY 0xBF
 
 /* Address Space (Operation Region) Types */
 
@@ -786,6 +795,15 @@ typedef u8 acpi_adr_space_type;
 #define ACPI_ENABLE_EVENT                       1
 #define ACPI_DISABLE_EVENT                      0
 
+/* Sleep function dispatch */
+
+typedef acpi_status(*ACPI_SLEEP_FUNCTION) (u8 sleep_state, u8 flags);
+
+struct acpi_sleep_functions {
+       ACPI_SLEEP_FUNCTION legacy_function;
+       ACPI_SLEEP_FUNCTION extended_function;
+};
+
 /*
  * External ACPI object definition
  */
index f53fea61f40a4edc0456f11ae27586ebf1a3eb6f..f421dd84f29d375468c3317db9cc4a69e0f9e33d 100644 (file)
@@ -372,4 +372,14 @@ static inline int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *),
 
 #endif /* !CONFIG_ACPI */
 
+#ifdef CONFIG_ACPI
+void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state,
+                              u32 pm1a_ctrl,  u32 pm1b_ctrl));
+
+acpi_status acpi_os_prepare_sleep(u8 sleep_state,
+                                 u32 pm1a_control, u32 pm1b_control);
+#else
+#define acpi_os_set_prepare_sleep(func, pm1a_ctrl, pm1b_ctrl) do { } while (0)
+#endif
+
 #endif /*_LINUX_ACPI_H*/
index 712abcc205ae0f4a68bda6c4da51e22a7b4000d3..6c26a3da0e03173b29ce1535c9833537f85a6786 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/list.h>
 #include <linux/kobject.h>
 #include <linux/completion.h>
+#include <linux/hrtimer.h>
 
 #define CPUIDLE_STATE_MAX      8
 #define CPUIDLE_NAME_LEN       16
@@ -43,12 +44,15 @@ struct cpuidle_state {
 
        unsigned int    flags;
        unsigned int    exit_latency; /* in US */
-       unsigned int    power_usage; /* in mW */
+       int             power_usage; /* in mW */
        unsigned int    target_residency; /* in US */
+       unsigned int    disable;
 
        int (*enter)    (struct cpuidle_device *dev,
                        struct cpuidle_driver *drv,
                        int index);
+
+       int (*enter_dead) (struct cpuidle_device *dev, int index);
 };
 
 /* Idle State Flags */
@@ -96,7 +100,6 @@ struct cpuidle_device {
        struct list_head        device_list;
        struct kobject          kobj;
        struct completion       kobj_unregister;
-       void                    *governor_data;
 };
 
 DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
@@ -118,10 +121,12 @@ static inline int cpuidle_get_last_residency(struct cpuidle_device *dev)
  ****************************/
 
 struct cpuidle_driver {
-       char                    name[CPUIDLE_NAME_LEN];
+       const char              *name;
        struct module           *owner;
 
        unsigned int            power_specified:1;
+       /* set to 1 to use the core cpuidle time keeping (for all states). */
+       unsigned int            en_core_tk_irqen:1;
        struct cpuidle_state    states[CPUIDLE_STATE_MAX];
        int                     state_count;
        int                     safe_state_index;
@@ -140,6 +145,11 @@ extern void cpuidle_pause_and_lock(void);
 extern void cpuidle_resume_and_unlock(void);
 extern int cpuidle_enable_device(struct cpuidle_device *dev);
 extern void cpuidle_disable_device(struct cpuidle_device *dev);
+extern int cpuidle_wrap_enter(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int index,
+                               int (*enter)(struct cpuidle_device *dev,
+                                       struct cpuidle_driver *drv, int index));
+extern int cpuidle_play_dead(void);
 
 #else
 static inline void disable_cpuidle(void) { }
@@ -157,6 +167,12 @@ static inline void cpuidle_resume_and_unlock(void) { }
 static inline int cpuidle_enable_device(struct cpuidle_device *dev)
 {return -ENODEV; }
 static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
+static inline int cpuidle_wrap_enter(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int index,
+                               int (*enter)(struct cpuidle_device *dev,
+                                       struct cpuidle_driver *drv, int index))
+{ return -ENODEV; }
+static inline int cpuidle_play_dead(void) {return -ENODEV; }
 
 #endif
 
index dd478fc8f9f55deb1194d19894ff6ad3c040874a..5f3f3be5af09b6446026f32c0845d842c257b629 100644 (file)
@@ -144,12 +144,14 @@ struct event_filter;
 enum trace_reg {
        TRACE_REG_REGISTER,
        TRACE_REG_UNREGISTER,
+#ifdef CONFIG_PERF_EVENTS
        TRACE_REG_PERF_REGISTER,
        TRACE_REG_PERF_UNREGISTER,
        TRACE_REG_PERF_OPEN,
        TRACE_REG_PERF_CLOSE,
        TRACE_REG_PERF_ADD,
        TRACE_REG_PERF_DEL,
+#endif
 };
 
 struct ftrace_event_call;
index 004ff33ab38e4dc3e2135f01ebe6b0d84fbb1529..a7e977ff4abf060190118884b8a107c826c78feb 100644 (file)
@@ -6,7 +6,7 @@ struct device;
 struct gpio_keys_button {
        /* Configuration parameters */
        unsigned int code;      /* input event code (KEY_*, SW_*) */
-       int gpio;
+       int gpio;               /* -1 if this key does not support gpio */
        int active_low;
        const char *desc;
        unsigned int type;      /* input event type (EV_KEY, EV_SW, EV_ABS) */
@@ -14,6 +14,7 @@ struct gpio_keys_button {
        int debounce_interval;  /* debounce ticks interval in msecs */
        bool can_disable;
        int value;              /* axis value for EV_ABS */
+       unsigned int irq;       /* Irq number in case of interrupt keys */
 };
 
 struct gpio_keys_platform_data {
index a5375e7f3feac423fa6325280276e17dcf5351f6..645231c373c8a5b5684e806a9d13647d1eb3300e 100644 (file)
@@ -430,16 +430,10 @@ extern int __must_check hex2bin(u8 *dst, const char *src, size_t count);
  * Most likely, you want to use tracing_on/tracing_off.
  */
 #ifdef CONFIG_RING_BUFFER
-void tracing_on(void);
-void tracing_off(void);
 /* trace_off_permanent stops recording with no way to bring it back */
 void tracing_off_permanent(void);
-int tracing_is_on(void);
 #else
-static inline void tracing_on(void) { }
-static inline void tracing_off(void) { }
 static inline void tracing_off_permanent(void) { }
-static inline int tracing_is_on(void) { return 0; }
 #endif
 
 enum ftrace_dump_mode {
@@ -449,6 +443,10 @@ enum ftrace_dump_mode {
 };
 
 #ifdef CONFIG_TRACING
+void tracing_on(void);
+void tracing_off(void);
+int tracing_is_on(void);
+
 extern void tracing_start(void);
 extern void tracing_stop(void);
 extern void ftrace_off_permanent(void);
@@ -533,6 +531,11 @@ static inline void tracing_start(void) { }
 static inline void tracing_stop(void) { }
 static inline void ftrace_off_permanent(void) { }
 static inline void trace_dump_stack(void) { }
+
+static inline void tracing_on(void) { }
+static inline void tracing_off(void) { }
+static inline int tracing_is_on(void) { return 0; }
+
 static inline int
 trace_printk(const char *fmt, ...)
 {
index d21fa2865bf4da4aa2873adc081091b840b6cf08..ea98c6133d32c694b57641906a146163f50a2af0 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * LP8727 Micro/Mini USB IC with integrated charger
+ *
+ *                     Copyright (C) 2011 Texas Instruments
  *                     Copyright (C) 2011 National Semiconductor
  *
  * This program is free software; you can redistribute it and/or modify
@@ -32,13 +35,24 @@ enum lp8727_ichg {
        ICHG_1000mA,
 };
 
+/**
+ * struct lp8727_chg_param
+ * @eoc_level : end of charge level setting
+ * @ichg : charging current
+ */
 struct lp8727_chg_param {
-       /* end of charge level setting */
        enum lp8727_eoc_level eoc_level;
-       /* charging current */
        enum lp8727_ichg ichg;
 };
 
+/**
+ * struct lp8727_platform_data
+ * @get_batt_present : check battery status - exists or not
+ * @get_batt_level : get battery voltage (mV)
+ * @get_batt_capacity : get battery capacity (%)
+ * @get_batt_temp : get battery temperature
+ * @ac, @usb : charging parameters each charger type
+ */
 struct lp8727_platform_data {
        u8 (*get_batt_present)(void);
        u16 (*get_batt_level)(void);
index 5fa697477b71fa6d6fe14fecf3f679bb22851b9a..ee96cd51d8b23b54683950a50831cb5cda65d2ac 100644 (file)
@@ -146,6 +146,279 @@ struct abx500_init_settings {
        u8 setting;
 };
 
+/* Battery driver related data */
+/*
+ * ADC for the battery thermistor.
+ * When using the ABx500_ADC_THERM_BATCTRL the battery ID resistor is combined
+ * with a NTC resistor to both identify the battery and to measure its
+ * temperature. Different phone manufactures uses different techniques to both
+ * identify the battery and to read its temperature.
+ */
+enum abx500_adc_therm {
+       ABx500_ADC_THERM_BATCTRL,
+       ABx500_ADC_THERM_BATTEMP,
+};
+
+/**
+ * struct abx500_res_to_temp - defines one point in a temp to res curve. To
+ * be used in battery packs that combines the identification resistor with a
+ * NTC resistor.
+ * @temp:                      battery pack temperature in Celcius
+ * @resist:                    NTC resistor net total resistance
+ */
+struct abx500_res_to_temp {
+       int temp;
+       int resist;
+};
+
+/**
+ * struct abx500_v_to_cap - Table for translating voltage to capacity
+ * @voltage:           Voltage in mV
+ * @capacity:          Capacity in percent
+ */
+struct abx500_v_to_cap {
+       int voltage;
+       int capacity;
+};
+
+/* Forward declaration */
+struct abx500_fg;
+
+/**
+ * struct abx500_fg_parameters - Fuel gauge algorithm parameters, in seconds
+ * if not specified
+ * @recovery_sleep_timer:      Time between measurements while recovering
+ * @recovery_total_time:       Total recovery time
+ * @init_timer:                        Measurement interval during startup
+ * @init_discard_time:         Time we discard voltage measurement at startup
+ * @init_total_time:           Total init time during startup
+ * @high_curr_time:            Time current has to be high to go to recovery
+ * @accu_charging:             FG accumulation time while charging
+ * @accu_high_curr:            FG accumulation time in high current mode
+ * @high_curr_threshold:       High current threshold, in mA
+ * @lowbat_threshold:          Low battery threshold, in mV
+ * @overbat_threshold:         Over battery threshold, in mV
+ * @battok_falling_th_sel0     Threshold in mV for battOk signal sel0
+ *                             Resolution in 50 mV step.
+ * @battok_raising_th_sel1     Threshold in mV for battOk signal sel1
+ *                             Resolution in 50 mV step.
+ * @user_cap_limit             Capacity reported from user must be within this
+ *                             limit to be considered as sane, in percentage
+ *                             points.
+ * @maint_thres                        This is the threshold where we stop reporting
+ *                             battery full while in maintenance, in per cent
+ */
+struct abx500_fg_parameters {
+       int recovery_sleep_timer;
+       int recovery_total_time;
+       int init_timer;
+       int init_discard_time;
+       int init_total_time;
+       int high_curr_time;
+       int accu_charging;
+       int accu_high_curr;
+       int high_curr_threshold;
+       int lowbat_threshold;
+       int overbat_threshold;
+       int battok_falling_th_sel0;
+       int battok_raising_th_sel1;
+       int user_cap_limit;
+       int maint_thres;
+};
+
+/**
+ * struct abx500_charger_maximization - struct used by the board config.
+ * @use_maxi:          Enable maximization for this battery type
+ * @maxi_chg_curr:     Maximum charger current allowed
+ * @maxi_wait_cycles:  cycles to wait before setting charger current
+ * @charger_curr_step  delta between two charger current settings (mA)
+ */
+struct abx500_maxim_parameters {
+       bool ena_maxi;
+       int chg_curr;
+       int wait_cycles;
+       int charger_curr_step;
+};
+
+/**
+ * struct abx500_battery_type - different batteries supported
+ * @name:                      battery technology
+ * @resis_high:                        battery upper resistance limit
+ * @resis_low:                 battery lower resistance limit
+ * @charge_full_design:                Maximum battery capacity in mAh
+ * @nominal_voltage:           Nominal voltage of the battery in mV
+ * @termination_vol:           max voltage upto which battery can be charged
+ * @termination_curr           battery charging termination current in mA
+ * @recharge_vol               battery voltage limit that will trigger a new
+ *                             full charging cycle in the case where maintenan-
+ *                             -ce charging has been disabled
+ * @normal_cur_lvl:            charger current in normal state in mA
+ * @normal_vol_lvl:            charger voltage in normal state in mV
+ * @maint_a_cur_lvl:           charger current in maintenance A state in mA
+ * @maint_a_vol_lvl:           charger voltage in maintenance A state in mV
+ * @maint_a_chg_timer_h:       charge time in maintenance A state
+ * @maint_b_cur_lvl:           charger current in maintenance B state in mA
+ * @maint_b_vol_lvl:           charger voltage in maintenance B state in mV
+ * @maint_b_chg_timer_h:       charge time in maintenance B state
+ * @low_high_cur_lvl:          charger current in temp low/high state in mA
+ * @low_high_vol_lvl:          charger voltage in temp low/high state in mV'
+ * @battery_resistance:                battery inner resistance in mOhm.
+ * @n_r_t_tbl_elements:                number of elements in r_to_t_tbl
+ * @r_to_t_tbl:                        table containing resistance to temp points
+ * @n_v_cap_tbl_elements:      number of elements in v_to_cap_tbl
+ * @v_to_cap_tbl:              Voltage to capacity (in %) table
+ * @n_batres_tbl_elements      number of elements in the batres_tbl
+ * @batres_tbl                 battery internal resistance vs temperature table
+ */
+struct abx500_battery_type {
+       int name;
+       int resis_high;
+       int resis_low;
+       int charge_full_design;
+       int nominal_voltage;
+       int termination_vol;
+       int termination_curr;
+       int recharge_vol;
+       int normal_cur_lvl;
+       int normal_vol_lvl;
+       int maint_a_cur_lvl;
+       int maint_a_vol_lvl;
+       int maint_a_chg_timer_h;
+       int maint_b_cur_lvl;
+       int maint_b_vol_lvl;
+       int maint_b_chg_timer_h;
+       int low_high_cur_lvl;
+       int low_high_vol_lvl;
+       int battery_resistance;
+       int n_temp_tbl_elements;
+       struct abx500_res_to_temp *r_to_t_tbl;
+       int n_v_cap_tbl_elements;
+       struct abx500_v_to_cap *v_to_cap_tbl;
+       int n_batres_tbl_elements;
+       struct batres_vs_temp *batres_tbl;
+};
+
+/**
+ * struct abx500_bm_capacity_levels - abx500 capacity level data
+ * @critical:          critical capacity level in percent
+ * @low:               low capacity level in percent
+ * @normal:            normal capacity level in percent
+ * @high:              high capacity level in percent
+ * @full:              full capacity level in percent
+ */
+struct abx500_bm_capacity_levels {
+       int critical;
+       int low;
+       int normal;
+       int high;
+       int full;
+};
+
+/**
+ * struct abx500_bm_charger_parameters - Charger specific parameters
+ * @usb_volt_max:      maximum allowed USB charger voltage in mV
+ * @usb_curr_max:      maximum allowed USB charger current in mA
+ * @ac_volt_max:       maximum allowed AC charger voltage in mV
+ * @ac_curr_max:       maximum allowed AC charger current in mA
+ */
+struct abx500_bm_charger_parameters {
+       int usb_volt_max;
+       int usb_curr_max;
+       int ac_volt_max;
+       int ac_curr_max;
+};
+
+/**
+ * struct abx500_bm_data - abx500 battery management data
+ * @temp_under         under this temp, charging is stopped
+ * @temp_low           between this temp and temp_under charging is reduced
+ * @temp_high          between this temp and temp_over charging is reduced
+ * @temp_over          over this temp, charging is stopped
+ * @temp_now           present battery temperature
+ * @temp_interval_chg  temperature measurement interval in s when charging
+ * @temp_interval_nochg        temperature measurement interval in s when not charging
+ * @main_safety_tmr_h  safety timer for main charger
+ * @usb_safety_tmr_h   safety timer for usb charger
+ * @bkup_bat_v         voltage which we charge the backup battery with
+ * @bkup_bat_i         current which we charge the backup battery with
+ * @no_maintenance     indicates that maintenance charging is disabled
+ * @abx500_adc_therm   placement of thermistor, batctrl or battemp adc
+ * @chg_unknown_bat    flag to enable charging of unknown batteries
+ * @enable_overshoot   flag to enable VBAT overshoot control
+ * @auto_trig          flag to enable auto adc trigger
+ * @fg_res             resistance of FG resistor in 0.1mOhm
+ * @n_btypes           number of elements in array bat_type
+ * @batt_id            index of the identified battery in array bat_type
+ * @interval_charging  charge alg cycle period time when charging (sec)
+ * @interval_not_charging charge alg cycle period time when not charging (sec)
+ * @temp_hysteresis    temperature hysteresis
+ * @gnd_lift_resistance        Battery ground to phone ground resistance (mOhm)
+ * @maxi:              maximization parameters
+ * @cap_levels         capacity in percent for the different capacity levels
+ * @bat_type           table of supported battery types
+ * @chg_params         charger parameters
+ * @fg_params          fuel gauge parameters
+ */
+struct abx500_bm_data {
+       int temp_under;
+       int temp_low;
+       int temp_high;
+       int temp_over;
+       int temp_now;
+       int temp_interval_chg;
+       int temp_interval_nochg;
+       int main_safety_tmr_h;
+       int usb_safety_tmr_h;
+       int bkup_bat_v;
+       int bkup_bat_i;
+       bool no_maintenance;
+       bool chg_unknown_bat;
+       bool enable_overshoot;
+       bool auto_trig;
+       enum abx500_adc_therm adc_therm;
+       int fg_res;
+       int n_btypes;
+       int batt_id;
+       int interval_charging;
+       int interval_not_charging;
+       int temp_hysteresis;
+       int gnd_lift_resistance;
+       const struct abx500_maxim_parameters *maxi;
+       const struct abx500_bm_capacity_levels *cap_levels;
+       const struct abx500_battery_type *bat_type;
+       const struct abx500_bm_charger_parameters *chg_params;
+       const struct abx500_fg_parameters *fg_params;
+};
+
+struct abx500_chargalg_platform_data {
+       char **supplied_to;
+       size_t num_supplicants;
+};
+
+struct abx500_charger_platform_data {
+       char **supplied_to;
+       size_t num_supplicants;
+       bool autopower_cfg;
+};
+
+struct abx500_btemp_platform_data {
+       char **supplied_to;
+       size_t num_supplicants;
+};
+
+struct abx500_fg_platform_data {
+       char **supplied_to;
+       size_t num_supplicants;
+};
+
+struct abx500_bm_plat_data {
+       struct abx500_bm_data *battery;
+       struct abx500_charger_platform_data *charger;
+       struct abx500_btemp_platform_data *btemp;
+       struct abx500_fg_platform_data *fg;
+       struct abx500_chargalg_platform_data *chargalg;
+};
+
 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,
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
new file mode 100644 (file)
index 0000000..44310c9
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * Copyright ST-Ericsson 2012.
+ *
+ * Author: Arun Murthy <arun.murthy@stericsson.com>
+ * Licensed under GPLv2.
+ */
+
+#ifndef _AB8500_BM_H
+#define _AB8500_BM_H
+
+#include <linux/kernel.h>
+#include <linux/mfd/abx500.h>
+
+/*
+ * System control 2 register offsets.
+ * bank = 0x02
+ */
+#define AB8500_MAIN_WDOG_CTRL_REG      0x01
+#define AB8500_LOW_BAT_REG             0x03
+#define AB8500_BATT_OK_REG             0x04
+/*
+ * USB/ULPI register offsets
+ * Bank : 0x5
+ */
+#define AB8500_USB_LINE_STAT_REG       0x80
+
+/*
+ * Charger / status register offfsets
+ * Bank : 0x0B
+ */
+#define AB8500_CH_STATUS1_REG          0x00
+#define AB8500_CH_STATUS2_REG          0x01
+#define AB8500_CH_USBCH_STAT1_REG      0x02
+#define AB8500_CH_USBCH_STAT2_REG      0x03
+#define AB8500_CH_FSM_STAT_REG         0x04
+#define AB8500_CH_STAT_REG             0x05
+
+/*
+ * Charger / control register offfsets
+ * Bank : 0x0B
+ */
+#define AB8500_CH_VOLT_LVL_REG         0x40
+#define AB8500_CH_VOLT_LVL_MAX_REG     0x41  /*Only in Cut2.0*/
+#define AB8500_CH_OPT_CRNTLVL_REG      0x42
+#define AB8500_CH_OPT_CRNTLVL_MAX_REG  0x43  /*Only in Cut2.0*/
+#define AB8500_CH_WD_TIMER_REG         0x50
+#define AB8500_CHARG_WD_CTRL           0x51
+#define AB8500_BTEMP_HIGH_TH           0x52
+#define AB8500_LED_INDICATOR_PWM_CTRL  0x53
+#define AB8500_LED_INDICATOR_PWM_DUTY  0x54
+#define AB8500_BATT_OVV                        0x55
+#define AB8500_CHARGER_CTRL            0x56
+#define AB8500_BAT_CTRL_CURRENT_SOURCE 0x60  /*Only in Cut2.0*/
+
+/*
+ * Charger / main control register offsets
+ * Bank : 0x0B
+ */
+#define AB8500_MCH_CTRL1               0x80
+#define AB8500_MCH_CTRL2               0x81
+#define AB8500_MCH_IPT_CURLVL_REG      0x82
+#define AB8500_CH_WD_REG               0x83
+
+/*
+ * Charger / USB control register offsets
+ * Bank : 0x0B
+ */
+#define AB8500_USBCH_CTRL1_REG         0xC0
+#define AB8500_USBCH_CTRL2_REG         0xC1
+#define AB8500_USBCH_IPT_CRNTLVL_REG   0xC2
+
+/*
+ * Gas Gauge register offsets
+ * Bank : 0x0C
+ */
+#define AB8500_GASG_CC_CTRL_REG                0x00
+#define AB8500_GASG_CC_ACCU1_REG       0x01
+#define AB8500_GASG_CC_ACCU2_REG       0x02
+#define AB8500_GASG_CC_ACCU3_REG       0x03
+#define AB8500_GASG_CC_ACCU4_REG       0x04
+#define AB8500_GASG_CC_SMPL_CNTRL_REG  0x05
+#define AB8500_GASG_CC_SMPL_CNTRH_REG  0x06
+#define AB8500_GASG_CC_SMPL_CNVL_REG   0x07
+#define AB8500_GASG_CC_SMPL_CNVH_REG   0x08
+#define AB8500_GASG_CC_CNTR_AVGOFF_REG 0x09
+#define AB8500_GASG_CC_OFFSET_REG      0x0A
+#define AB8500_GASG_CC_NCOV_ACCU       0x10
+#define AB8500_GASG_CC_NCOV_ACCU_CTRL  0x11
+#define AB8500_GASG_CC_NCOV_ACCU_LOW   0x12
+#define AB8500_GASG_CC_NCOV_ACCU_MED   0x13
+#define AB8500_GASG_CC_NCOV_ACCU_HIGH  0x14
+
+/*
+ * Interrupt register offsets
+ * Bank : 0x0E
+ */
+#define AB8500_IT_SOURCE2_REG          0x01
+#define AB8500_IT_SOURCE21_REG         0x14
+
+/*
+ * RTC register offsets
+ * Bank: 0x0F
+ */
+#define AB8500_RTC_BACKUP_CHG_REG      0x0C
+#define AB8500_RTC_CC_CONF_REG         0x01
+#define AB8500_RTC_CTRL_REG            0x0B
+
+/*
+ * OTP register offsets
+ * Bank : 0x15
+ */
+#define AB8500_OTP_CONF_15             0x0E
+
+/* GPADC constants from AB8500 spec, UM0836 */
+#define ADC_RESOLUTION                 1024
+#define ADC_CH_MAIN_MIN                        0
+#define ADC_CH_MAIN_MAX                        20030
+#define ADC_CH_VBUS_MIN                        0
+#define ADC_CH_VBUS_MAX                        20030
+#define ADC_CH_VBAT_MIN                        2300
+#define ADC_CH_VBAT_MAX                        4800
+#define ADC_CH_BKBAT_MIN               0
+#define ADC_CH_BKBAT_MAX               3200
+
+/* Main charge i/p current */
+#define MAIN_CH_IP_CUR_0P9A            0x80
+#define MAIN_CH_IP_CUR_1P0A            0x90
+#define MAIN_CH_IP_CUR_1P1A            0xA0
+#define MAIN_CH_IP_CUR_1P2A            0xB0
+#define MAIN_CH_IP_CUR_1P3A            0xC0
+#define MAIN_CH_IP_CUR_1P4A            0xD0
+#define MAIN_CH_IP_CUR_1P5A            0xE0
+
+/* ChVoltLevel */
+#define CH_VOL_LVL_3P5                 0x00
+#define CH_VOL_LVL_4P0                 0x14
+#define CH_VOL_LVL_4P05                        0x16
+#define CH_VOL_LVL_4P1                 0x1B
+#define CH_VOL_LVL_4P15                        0x20
+#define CH_VOL_LVL_4P2                 0x25
+#define CH_VOL_LVL_4P6                 0x4D
+
+/* ChOutputCurrentLevel */
+#define CH_OP_CUR_LVL_0P1              0x00
+#define CH_OP_CUR_LVL_0P2              0x01
+#define CH_OP_CUR_LVL_0P3              0x02
+#define CH_OP_CUR_LVL_0P4              0x03
+#define CH_OP_CUR_LVL_0P5              0x04
+#define CH_OP_CUR_LVL_0P6              0x05
+#define CH_OP_CUR_LVL_0P7              0x06
+#define CH_OP_CUR_LVL_0P8              0x07
+#define CH_OP_CUR_LVL_0P9              0x08
+#define CH_OP_CUR_LVL_1P4              0x0D
+#define CH_OP_CUR_LVL_1P5              0x0E
+#define CH_OP_CUR_LVL_1P6              0x0F
+
+/* BTEMP High thermal limits */
+#define BTEMP_HIGH_TH_57_0             0x00
+#define BTEMP_HIGH_TH_52               0x01
+#define BTEMP_HIGH_TH_57_1             0x02
+#define BTEMP_HIGH_TH_62               0x03
+
+/* current is mA */
+#define USB_0P1A                       100
+#define USB_0P2A                       200
+#define USB_0P3A                       300
+#define USB_0P4A                       400
+#define USB_0P5A                       500
+
+#define LOW_BAT_3P1V                   0x20
+#define LOW_BAT_2P3V                   0x00
+#define LOW_BAT_RESET                  0x01
+#define LOW_BAT_ENABLE                 0x01
+
+/* Backup battery constants */
+#define BUP_ICH_SEL_50UA               0x00
+#define BUP_ICH_SEL_150UA              0x04
+#define BUP_ICH_SEL_300UA              0x08
+#define BUP_ICH_SEL_700UA              0x0C
+
+#define BUP_VCH_SEL_2P5V               0x00
+#define BUP_VCH_SEL_2P6V               0x01
+#define BUP_VCH_SEL_2P8V               0x02
+#define BUP_VCH_SEL_3P1V               0x03
+
+/* Battery OVV constants */
+#define BATT_OVV_ENA                   0x02
+#define BATT_OVV_TH_3P7                        0x00
+#define BATT_OVV_TH_4P75               0x01
+
+/* A value to indicate over voltage */
+#define BATT_OVV_VALUE                 4750
+
+/* VBUS OVV constants */
+#define VBUS_OVV_SELECT_MASK           0x78
+#define VBUS_OVV_SELECT_5P6V           0x00
+#define VBUS_OVV_SELECT_5P7V           0x08
+#define VBUS_OVV_SELECT_5P8V           0x10
+#define VBUS_OVV_SELECT_5P9V           0x18
+#define VBUS_OVV_SELECT_6P0V           0x20
+#define VBUS_OVV_SELECT_6P1V           0x28
+#define VBUS_OVV_SELECT_6P2V           0x30
+#define VBUS_OVV_SELECT_6P3V           0x38
+
+#define VBUS_AUTO_IN_CURR_LIM_ENA      0x04
+
+/* Fuel Gauge constants */
+#define RESET_ACCU                     0x02
+#define READ_REQ                       0x01
+#define CC_DEEP_SLEEP_ENA              0x02
+#define CC_PWR_UP_ENA                  0x01
+#define CC_SAMPLES_40                  0x28
+#define RD_NCONV_ACCU_REQ              0x01
+#define CC_CALIB                       0x08
+#define CC_INTAVGOFFSET_ENA            0x10
+#define CC_MUXOFFSET                   0x80
+#define CC_INT_CAL_N_AVG_MASK          0x60
+#define CC_INT_CAL_SAMPLES_16          0x40
+#define CC_INT_CAL_SAMPLES_8           0x20
+#define CC_INT_CAL_SAMPLES_4           0x00
+
+/* RTC constants */
+#define RTC_BUP_CH_ENA                 0x10
+
+/* BatCtrl Current Source Constants */
+#define BAT_CTRL_7U_ENA                        0x01
+#define BAT_CTRL_20U_ENA               0x02
+#define BAT_CTRL_CMP_ENA               0x04
+#define FORCE_BAT_CTRL_CMP_HIGH                0x08
+#define BAT_CTRL_PULL_UP_ENA           0x10
+
+/* Battery type */
+#define BATTERY_UNKNOWN                        00
+
+/**
+ * struct res_to_temp - defines one point in a temp to res curve. To
+ * be used in battery packs that combines the identification resistor with a
+ * NTC resistor.
+ * @temp:                      battery pack temperature in Celcius
+ * @resist:                    NTC resistor net total resistance
+ */
+struct res_to_temp {
+       int temp;
+       int resist;
+};
+
+/**
+ * struct batres_vs_temp - defines one point in a temp vs battery internal
+ * resistance curve.
+ * @temp:                      battery pack temperature in Celcius
+ * @resist:                    battery internal reistance in mOhm
+ */
+struct batres_vs_temp {
+       int temp;
+       int resist;
+};
+
+/* Forward declaration */
+struct ab8500_fg;
+
+/**
+ * struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds
+ * if not specified
+ * @recovery_sleep_timer:      Time between measurements while recovering
+ * @recovery_total_time:       Total recovery time
+ * @init_timer:                        Measurement interval during startup
+ * @init_discard_time:         Time we discard voltage measurement at startup
+ * @init_total_time:           Total init time during startup
+ * @high_curr_time:            Time current has to be high to go to recovery
+ * @accu_charging:             FG accumulation time while charging
+ * @accu_high_curr:            FG accumulation time in high current mode
+ * @high_curr_threshold:       High current threshold, in mA
+ * @lowbat_threshold:          Low battery threshold, in mV
+ * @battok_falling_th_sel0     Threshold in mV for battOk signal sel0
+ *                             Resolution in 50 mV step.
+ * @battok_raising_th_sel1     Threshold in mV for battOk signal sel1
+ *                             Resolution in 50 mV step.
+ * @user_cap_limit             Capacity reported from user must be within this
+ *                             limit to be considered as sane, in percentage
+ *                             points.
+ * @maint_thres                        This is the threshold where we stop reporting
+ *                             battery full while in maintenance, in per cent
+ */
+struct ab8500_fg_parameters {
+       int recovery_sleep_timer;
+       int recovery_total_time;
+       int init_timer;
+       int init_discard_time;
+       int init_total_time;
+       int high_curr_time;
+       int accu_charging;
+       int accu_high_curr;
+       int high_curr_threshold;
+       int lowbat_threshold;
+       int battok_falling_th_sel0;
+       int battok_raising_th_sel1;
+       int user_cap_limit;
+       int maint_thres;
+};
+
+/**
+ * struct ab8500_charger_maximization - struct used by the board config.
+ * @use_maxi:          Enable maximization for this battery type
+ * @maxi_chg_curr:     Maximum charger current allowed
+ * @maxi_wait_cycles:  cycles to wait before setting charger current
+ * @charger_curr_step  delta between two charger current settings (mA)
+ */
+struct ab8500_maxim_parameters {
+       bool ena_maxi;
+       int chg_curr;
+       int wait_cycles;
+       int charger_curr_step;
+};
+
+/**
+ * struct ab8500_bm_capacity_levels - ab8500 capacity level data
+ * @critical:          critical capacity level in percent
+ * @low:               low capacity level in percent
+ * @normal:            normal capacity level in percent
+ * @high:              high capacity level in percent
+ * @full:              full capacity level in percent
+ */
+struct ab8500_bm_capacity_levels {
+       int critical;
+       int low;
+       int normal;
+       int high;
+       int full;
+};
+
+/**
+ * struct ab8500_bm_charger_parameters - Charger specific parameters
+ * @usb_volt_max:      maximum allowed USB charger voltage in mV
+ * @usb_curr_max:      maximum allowed USB charger current in mA
+ * @ac_volt_max:       maximum allowed AC charger voltage in mV
+ * @ac_curr_max:       maximum allowed AC charger current in mA
+ */
+struct ab8500_bm_charger_parameters {
+       int usb_volt_max;
+       int usb_curr_max;
+       int ac_volt_max;
+       int ac_curr_max;
+};
+
+/**
+ * struct ab8500_bm_data - ab8500 battery management data
+ * @temp_under         under this temp, charging is stopped
+ * @temp_low           between this temp and temp_under charging is reduced
+ * @temp_high          between this temp and temp_over charging is reduced
+ * @temp_over          over this temp, charging is stopped
+ * @temp_interval_chg  temperature measurement interval in s when charging
+ * @temp_interval_nochg        temperature measurement interval in s when not charging
+ * @main_safety_tmr_h  safety timer for main charger
+ * @usb_safety_tmr_h   safety timer for usb charger
+ * @bkup_bat_v         voltage which we charge the backup battery with
+ * @bkup_bat_i         current which we charge the backup battery with
+ * @no_maintenance     indicates that maintenance charging is disabled
+ * @adc_therm          placement of thermistor, batctrl or battemp adc
+ * @chg_unknown_bat    flag to enable charging of unknown batteries
+ * @enable_overshoot   flag to enable VBAT overshoot control
+ * @fg_res             resistance of FG resistor in 0.1mOhm
+ * @n_btypes           number of elements in array bat_type
+ * @batt_id            index of the identified battery in array bat_type
+ * @interval_charging  charge alg cycle period time when charging (sec)
+ * @interval_not_charging charge alg cycle period time when not charging (sec)
+ * @temp_hysteresis    temperature hysteresis
+ * @gnd_lift_resistance        Battery ground to phone ground resistance (mOhm)
+ * @maxi:              maximization parameters
+ * @cap_levels         capacity in percent for the different capacity levels
+ * @bat_type           table of supported battery types
+ * @chg_params         charger parameters
+ * @fg_params          fuel gauge parameters
+ */
+struct ab8500_bm_data {
+       int temp_under;
+       int temp_low;
+       int temp_high;
+       int temp_over;
+       int temp_interval_chg;
+       int temp_interval_nochg;
+       int main_safety_tmr_h;
+       int usb_safety_tmr_h;
+       int bkup_bat_v;
+       int bkup_bat_i;
+       bool no_maintenance;
+       bool chg_unknown_bat;
+       bool enable_overshoot;
+       enum abx500_adc_therm adc_therm;
+       int fg_res;
+       int n_btypes;
+       int batt_id;
+       int interval_charging;
+       int interval_not_charging;
+       int temp_hysteresis;
+       int gnd_lift_resistance;
+       const struct ab8500_maxim_parameters *maxi;
+       const struct ab8500_bm_capacity_levels *cap_levels;
+       const struct ab8500_bm_charger_parameters *chg_params;
+       const struct ab8500_fg_parameters *fg_params;
+};
+
+struct ab8500_charger_platform_data {
+       char **supplied_to;
+       size_t num_supplicants;
+       bool autopower_cfg;
+};
+
+struct ab8500_btemp_platform_data {
+       char **supplied_to;
+       size_t num_supplicants;
+};
+
+struct ab8500_fg_platform_data {
+       char **supplied_to;
+       size_t num_supplicants;
+};
+
+struct ab8500_chargalg_platform_data {
+       char **supplied_to;
+       size_t num_supplicants;
+};
+struct ab8500_btemp;
+struct ab8500_gpadc;
+struct ab8500_fg;
+#ifdef CONFIG_AB8500_BM
+void ab8500_fg_reinit(void);
+void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
+struct ab8500_btemp *ab8500_btemp_get(void);
+int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp);
+struct ab8500_fg *ab8500_fg_get(void);
+int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev);
+int ab8500_fg_inst_curr_start(struct ab8500_fg *di);
+int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res);
+int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
+
+#else
+int ab8500_fg_inst_curr_done(struct ab8500_fg *di)
+{
+}
+static void ab8500_fg_reinit(void)
+{
+}
+static void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA)
+{
+}
+static struct ab8500_btemp *ab8500_btemp_get(void)
+{
+       return NULL;
+}
+static int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp)
+{
+       return 0;
+}
+struct ab8500_fg *ab8500_fg_get(void)
+{
+       return NULL;
+}
+static int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev)
+{
+       return -ENODEV;
+}
+
+static inline int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
+{
+       return -ENODEV;
+}
+
+static inline int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
+{
+       return -ENODEV;
+}
+
+#endif
+#endif /* _AB8500_BM_H */
diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h
new file mode 100644 (file)
index 0000000..9b07725
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ * Author: Johan Gardsmark <johan.gardsmark@stericsson.com> for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _UX500_CHARGALG_H
+#define _UX500_CHARGALG_H
+
+#include <linux/power_supply.h>
+
+#define psy_to_ux500_charger(x) container_of((x), \
+               struct ux500_charger, psy)
+
+/* Forward declaration */
+struct ux500_charger;
+
+struct ux500_charger_ops {
+       int (*enable) (struct ux500_charger *, int, int, int);
+       int (*kick_wd) (struct ux500_charger *);
+       int (*update_curr) (struct ux500_charger *, int);
+};
+
+/**
+ * struct ux500_charger - power supply ux500 charger sub class
+ * @psy                        power supply base class
+ * @ops                        ux500 charger operations
+ * @max_out_volt       maximum output charger voltage in mV
+ * @max_out_curr       maximum output charger current in mA
+ */
+struct ux500_charger {
+       struct power_supply psy;
+       struct ux500_charger_ops ops;
+       int max_out_volt;
+       int max_out_curr;
+};
+
+#endif
index c4eec228eef9756c660b538018550ab6bfc8790e..650ef352f045f48ec9874b611faf18fc2a2b94cb 100644 (file)
@@ -112,6 +112,11 @@ struct nand_bbt_descr {
 #define NAND_BBT_USE_FLASH     0x00020000
 /* Do not store flash based bad block table in OOB area; store it in-band */
 #define NAND_BBT_NO_OOB                0x00040000
+/*
+ * Do not write new bad block markers to OOB; useful, e.g., when ECC covers
+ * entire spare area. Must be used with NAND_BBT_USE_FLASH.
+ */
+#define NAND_BBT_NO_OOB_BBM    0x00080000
 
 /*
  * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
index 1bbd9f28924564d188f2a01bc2c14a3ee49f691b..ed270bd2e4df0d724bda716234a418cec30822c0 100644 (file)
@@ -47,6 +47,7 @@ struct mtd_blktrans_dev {
        struct request_queue *rq;
        spinlock_t queue_lock;
        void *priv;
+       fmode_t file_mode;
 };
 
 struct mtd_blktrans_ops {
index 6987995ad3cf64a0e10ba74bf43ef4d706d73b5d..b20029221fb1b6e95a1d1a689ba56850d8837f56 100644 (file)
 #define FSMC_NAND_BW8          1
 #define FSMC_NAND_BW16         2
 
-/*
- * The placement of the Command Latch Enable (CLE) and
- * Address Latch Enable (ALE) is twisted around in the
- * SPEAR310 implementation.
- */
-#if defined(CONFIG_MACH_SPEAR310)
-#define PLAT_NAND_CLE          (1 << 17)
-#define PLAT_NAND_ALE          (1 << 16)
-#else
-#define PLAT_NAND_CLE          (1 << 16)
-#define PLAT_NAND_ALE          (1 << 17)
-#endif
-
 #define FSMC_MAX_NOR_BANKS     4
 #define FSMC_MAX_NAND_BANKS    4
 
 #define FSMC_FLASH_WIDTH8      1
 #define FSMC_FLASH_WIDTH16     2
 
-struct fsmc_nor_bank_regs {
-       uint32_t ctrl;
-       uint32_t ctrl_tim;
-};
-
-/* ctrl register definitions */
-#define BANK_ENABLE            (1 << 0)
-#define MUXED                  (1 << 1)
-#define NOR_DEV                        (2 << 2)
-#define WIDTH_8                        (0 << 4)
-#define WIDTH_16               (1 << 4)
-#define RSTPWRDWN              (1 << 6)
-#define WPROT                  (1 << 7)
-#define WRT_ENABLE             (1 << 12)
-#define WAIT_ENB               (1 << 13)
-
-/* ctrl_tim register definitions */
-
-struct fsmc_nand_bank_regs {
-       uint32_t pc;
-       uint32_t sts;
-       uint32_t comm;
-       uint32_t attrib;
-       uint32_t ioata;
-       uint32_t ecc1;
-       uint32_t ecc2;
-       uint32_t ecc3;
-};
-
+/* fsmc controller registers for NOR flash */
+#define CTRL                   0x0
+       /* ctrl register definitions */
+       #define BANK_ENABLE             (1 << 0)
+       #define MUXED                   (1 << 1)
+       #define NOR_DEV                 (2 << 2)
+       #define WIDTH_8                 (0 << 4)
+       #define WIDTH_16                (1 << 4)
+       #define RSTPWRDWN               (1 << 6)
+       #define WPROT                   (1 << 7)
+       #define WRT_ENABLE              (1 << 12)
+       #define WAIT_ENB                (1 << 13)
+
+#define CTRL_TIM               0x4
+       /* ctrl_tim register definitions */
+
+#define FSMC_NOR_BANK_SZ       0x8
 #define FSMC_NOR_REG_SIZE      0x40
 
-struct fsmc_regs {
-       struct fsmc_nor_bank_regs nor_bank_regs[FSMC_MAX_NOR_BANKS];
-       uint8_t reserved_1[0x40 - 0x20];
-       struct fsmc_nand_bank_regs bank_regs[FSMC_MAX_NAND_BANKS];
-       uint8_t reserved_2[0xfe0 - 0xc0];
-       uint32_t peripid0;                      /* 0xfe0 */
-       uint32_t peripid1;                      /* 0xfe4 */
-       uint32_t peripid2;                      /* 0xfe8 */
-       uint32_t peripid3;                      /* 0xfec */
-       uint32_t pcellid0;                      /* 0xff0 */
-       uint32_t pcellid1;                      /* 0xff4 */
-       uint32_t pcellid2;                      /* 0xff8 */
-       uint32_t pcellid3;                      /* 0xffc */
-};
+#define FSMC_NOR_REG(base, bank, reg)          (base + \
+                                               FSMC_NOR_BANK_SZ * (bank) + \
+                                               reg)
+
+/* fsmc controller registers for NAND flash */
+#define PC                     0x00
+       /* pc register definitions */
+       #define FSMC_RESET              (1 << 0)
+       #define FSMC_WAITON             (1 << 1)
+       #define FSMC_ENABLE             (1 << 2)
+       #define FSMC_DEVTYPE_NAND       (1 << 3)
+       #define FSMC_DEVWID_8           (0 << 4)
+       #define FSMC_DEVWID_16          (1 << 4)
+       #define FSMC_ECCEN              (1 << 6)
+       #define FSMC_ECCPLEN_512        (0 << 7)
+       #define FSMC_ECCPLEN_256        (1 << 7)
+       #define FSMC_TCLR_1             (1)
+       #define FSMC_TCLR_SHIFT         (9)
+       #define FSMC_TCLR_MASK          (0xF)
+       #define FSMC_TAR_1              (1)
+       #define FSMC_TAR_SHIFT          (13)
+       #define FSMC_TAR_MASK           (0xF)
+#define STS                    0x04
+       /* sts register definitions */
+       #define FSMC_CODE_RDY           (1 << 15)
+#define COMM                   0x08
+       /* comm register definitions */
+       #define FSMC_TSET_0             0
+       #define FSMC_TSET_SHIFT         0
+       #define FSMC_TSET_MASK          0xFF
+       #define FSMC_TWAIT_6            6
+       #define FSMC_TWAIT_SHIFT        8
+       #define FSMC_TWAIT_MASK         0xFF
+       #define FSMC_THOLD_4            4
+       #define FSMC_THOLD_SHIFT        16
+       #define FSMC_THOLD_MASK         0xFF
+       #define FSMC_THIZ_1             1
+       #define FSMC_THIZ_SHIFT         24
+       #define FSMC_THIZ_MASK          0xFF
+#define ATTRIB                 0x0C
+#define IOATA                  0x10
+#define ECC1                   0x14
+#define ECC2                   0x18
+#define ECC3                   0x1C
+#define FSMC_NAND_BANK_SZ      0x20
+
+#define FSMC_NAND_REG(base, bank, reg)         (base + FSMC_NOR_REG_SIZE + \
+                                               (FSMC_NAND_BANK_SZ * (bank)) + \
+                                               reg)
 
 #define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
 
-/* pc register definitions */
-#define FSMC_RESET             (1 << 0)
-#define FSMC_WAITON            (1 << 1)
-#define FSMC_ENABLE            (1 << 2)
-#define FSMC_DEVTYPE_NAND      (1 << 3)
-#define FSMC_DEVWID_8          (0 << 4)
-#define FSMC_DEVWID_16         (1 << 4)
-#define FSMC_ECCEN             (1 << 6)
-#define FSMC_ECCPLEN_512       (0 << 7)
-#define FSMC_ECCPLEN_256       (1 << 7)
-#define FSMC_TCLR_1            (1 << 9)
-#define FSMC_TAR_1             (1 << 13)
-
-/* sts register definitions */
-#define FSMC_CODE_RDY          (1 << 15)
-
-/* comm register definitions */
-#define FSMC_TSET_0            (0 << 0)
-#define FSMC_TWAIT_6           (6 << 8)
-#define FSMC_THOLD_4           (4 << 16)
-#define FSMC_THIZ_1            (1 << 24)
-
 /*
  * There are 13 bytes of ecc for every 512 byte block in FSMC version 8
  * and it has to be read consecutively and immediately after the 512
@@ -133,6 +121,20 @@ struct fsmc_eccplace {
        struct fsmc_nand_eccplace eccplace[MAX_ECCPLACE_ENTRIES];
 };
 
+struct fsmc_nand_timings {
+       uint8_t tclr;
+       uint8_t tar;
+       uint8_t thiz;
+       uint8_t thold;
+       uint8_t twait;
+       uint8_t tset;
+};
+
+enum access_mode {
+       USE_DMA_ACCESS = 1,
+       USE_WORD_ACCESS,
+};
+
 /**
  * fsmc_nand_platform_data - platform specific NAND controller config
  * @partitions: partition table for the platform, use a default fallback
@@ -146,12 +148,23 @@ struct fsmc_eccplace {
  * this may be set to NULL
  */
 struct fsmc_nand_platform_data {
+       struct fsmc_nand_timings *nand_timings;
        struct mtd_partition    *partitions;
        unsigned int            nr_partitions;
        unsigned int            options;
        unsigned int            width;
        unsigned int            bank;
+
+       /* CLE, ALE offsets */
+       unsigned int            cle_off;
+       unsigned int            ale_off;
+       enum access_mode        mode;
+
        void                    (*select_bank)(uint32_t bank, uint32_t busw);
+
+       /* priv structures for dma accesses */
+       void                    *read_dma_priv;
+       void                    *write_dma_priv;
 };
 
 extern int __init fsmc_nor_init(struct platform_device *pdev,
index d43dc25af82e23a8c6b18f5f3785b8ad4383c8c7..cf5ea8cdcf8e3a9a325da9a6f28f6fb120ea1319 100644 (file)
@@ -164,6 +164,9 @@ struct mtd_info {
        /* ECC layout structure pointer - read only! */
        struct nand_ecclayout *ecclayout;
 
+       /* max number of correctible bit errors per writesize */
+       unsigned int ecc_strength;
+
        /* Data for variable erase regions. If numeraseregions is zero,
         * it means that the whole device has erasesize as given above.
         */
@@ -174,52 +177,52 @@ struct mtd_info {
         * Do not call via these pointers, use corresponding mtd_*()
         * wrappers instead.
         */
-       int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
-       int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
-                     size_t *retlen, void **virt, resource_size_t *phys);
-       void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
-       unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
-                                           unsigned long len,
-                                           unsigned long offset,
-                                           unsigned long flags);
-       int (*read) (struct mtd_info *mtd, loff_t from, size_t len,
-                    size_t *retlen, u_char *buf);
-       int (*write) (struct mtd_info *mtd, loff_t to, size_t len,
-                     size_t *retlen, const u_char *buf);
-       int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
-                           size_t *retlen, const u_char *buf);
-       int (*read_oob) (struct mtd_info *mtd, loff_t from,
-                        struct mtd_oob_ops *ops);
-       int (*write_oob) (struct mtd_info *mtd, loff_t to,
+       int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
+       int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
+                      size_t *retlen, void **virt, resource_size_t *phys);
+       int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
+       unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,
+                                            unsigned long len,
+                                            unsigned long offset,
+                                            unsigned long flags);
+       int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,
+                     size_t *retlen, u_char *buf);
+       int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,
+                      size_t *retlen, const u_char *buf);
+       int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
+                            size_t *retlen, const u_char *buf);
+       int (*_read_oob) (struct mtd_info *mtd, loff_t from,
                          struct mtd_oob_ops *ops);
-       int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
-                                  size_t len);
-       int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
-                                  size_t len, size_t *retlen, u_char *buf);
-       int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
-                                  size_t len);
-       int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
-                                  size_t len, size_t *retlen, u_char *buf);
-       int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t to, size_t len,
-                                   size_t *retlen, u_char *buf);
-       int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
-                                  size_t len);
-       int (*writev) (struct mtd_info *mtd, const struct kvec *vecs,
+       int (*_write_oob) (struct mtd_info *mtd, loff_t to,
+                          struct mtd_oob_ops *ops);
+       int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
+                                   size_t len);
+       int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
+                                   size_t len, size_t *retlen, u_char *buf);
+       int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
+                                   size_t len);
+       int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
+                                   size_t len, size_t *retlen, u_char *buf);
+       int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
+                                    size_t len, size_t *retlen, u_char *buf);
+       int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
+                                   size_t len);
+       int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
                        unsigned long count, loff_t to, size_t *retlen);
-       void (*sync) (struct mtd_info *mtd);
-       int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
-       int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
-       int (*is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
-       int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
-       int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
-       int (*suspend) (struct mtd_info *mtd);
-       void (*resume) (struct mtd_info *mtd);
+       void (*_sync) (struct mtd_info *mtd);
+       int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
+       int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
+       int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
+       int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
+       int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
+       int (*_suspend) (struct mtd_info *mtd);
+       void (*_resume) (struct mtd_info *mtd);
        /*
         * If the driver is something smart, like UBI, it may need to maintain
         * its own reference counting. The below functions are only for driver.
         */
-       int (*get_device) (struct mtd_info *mtd);
-       void (*put_device) (struct mtd_info *mtd);
+       int (*_get_device) (struct mtd_info *mtd);
+       void (*_put_device) (struct mtd_info *mtd);
 
        /* Backing device capabilities for this device
         * - provides mmap capabilities
@@ -240,214 +243,75 @@ struct mtd_info {
        int usecount;
 };
 
-/*
- * Erase is an asynchronous operation.  Device drivers are supposed
- * to call instr->callback() whenever the operation completes, even
- * if it completes with a failure.
- * Callers are supposed to pass a callback function and wait for it
- * to be called before writing to the block.
- */
-static inline int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
-       return mtd->erase(mtd, instr);
-}
-
-/*
- * This stuff for eXecute-In-Place. phys is optional and may be set to NULL.
- */
-static inline int mtd_point(struct mtd_info *mtd, loff_t from, size_t len,
-                           size_t *retlen, void **virt, resource_size_t *phys)
-{
-       *retlen = 0;
-       if (!mtd->point)
-               return -EOPNOTSUPP;
-       return mtd->point(mtd, from, len, retlen, virt, phys);
-}
-
-/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
-static inline void mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
-{
-       return mtd->unpoint(mtd, from, len);
-}
-
-/*
- * Allow NOMMU mmap() to directly map the device (if not NULL)
- * - return the address to which the offset maps
- * - return -ENOSYS to indicate refusal to do the mapping
- */
-static inline unsigned long mtd_get_unmapped_area(struct mtd_info *mtd,
-                                                 unsigned long len,
-                                                 unsigned long offset,
-                                                 unsigned long flags)
-{
-       if (!mtd->get_unmapped_area)
-               return -EOPNOTSUPP;
-       return mtd->get_unmapped_area(mtd, len, offset, flags);
-}
-
-static inline int mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
-                          size_t *retlen, u_char *buf)
-{
-       return mtd->read(mtd, from, len, retlen, buf);
-}
-
-static inline int mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
-                           size_t *retlen, const u_char *buf)
-{
-       *retlen = 0;
-       if (!mtd->write)
-               return -EROFS;
-       return mtd->write(mtd, to, len, retlen, buf);
-}
-
-/*
- * In blackbox flight recorder like scenarios we want to make successful writes
- * in interrupt context. panic_write() is only intended to be called when its
- * known the kernel is about to panic and we need the write to succeed. Since
- * the kernel is not going to be running for much longer, this function can
- * break locks and delay to ensure the write succeeds (but not sleep).
- */
-static inline int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
-                                 size_t *retlen, const u_char *buf)
-{
-       *retlen = 0;
-       if (!mtd->panic_write)
-               return -EOPNOTSUPP;
-       return mtd->panic_write(mtd, to, len, retlen, buf);
-}
+int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
+int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+             void **virt, resource_size_t *phys);
+int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
+unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len,
+                                   unsigned long offset, unsigned long flags);
+int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+            u_char *buf);
+int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+             const u_char *buf);
+int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+                   const u_char *buf);
 
 static inline int mtd_read_oob(struct mtd_info *mtd, loff_t from,
                               struct mtd_oob_ops *ops)
 {
        ops->retlen = ops->oobretlen = 0;
-       if (!mtd->read_oob)
+       if (!mtd->_read_oob)
                return -EOPNOTSUPP;
-       return mtd->read_oob(mtd, from, ops);
+       return mtd->_read_oob(mtd, from, ops);
 }
 
 static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to,
                                struct mtd_oob_ops *ops)
 {
        ops->retlen = ops->oobretlen = 0;
-       if (!mtd->write_oob)
-               return -EOPNOTSUPP;
-       return mtd->write_oob(mtd, to, ops);
-}
-
-/*
- * Method to access the protection register area, present in some flash
- * devices. The user data is one time programmable but the factory data is read
- * only.
- */
-static inline int mtd_get_fact_prot_info(struct mtd_info *mtd,
-                                        struct otp_info *buf, size_t len)
-{
-       if (!mtd->get_fact_prot_info)
+       if (!mtd->_write_oob)
                return -EOPNOTSUPP;
-       return mtd->get_fact_prot_info(mtd, buf, len);
-}
-
-static inline int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
-                                        size_t len, size_t *retlen,
-                                        u_char *buf)
-{
-       *retlen = 0;
-       if (!mtd->read_fact_prot_reg)
-               return -EOPNOTSUPP;
-       return mtd->read_fact_prot_reg(mtd, from, len, retlen, buf);
-}
-
-static inline int mtd_get_user_prot_info(struct mtd_info *mtd,
-                                        struct otp_info *buf,
-                                        size_t len)
-{
-       if (!mtd->get_user_prot_info)
-               return -EOPNOTSUPP;
-       return mtd->get_user_prot_info(mtd, buf, len);
-}
-
-static inline int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
-                                        size_t len, size_t *retlen,
-                                        u_char *buf)
-{
-       *retlen = 0;
-       if (!mtd->read_user_prot_reg)
-               return -EOPNOTSUPP;
-       return mtd->read_user_prot_reg(mtd, from, len, retlen, buf);
-}
-
-static inline int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to,
-                                         size_t len, size_t *retlen,
-                                         u_char *buf)
-{
-       *retlen = 0;
-       if (!mtd->write_user_prot_reg)
-               return -EOPNOTSUPP;
-       return mtd->write_user_prot_reg(mtd, to, len, retlen, buf);
+       if (!(mtd->flags & MTD_WRITEABLE))
+               return -EROFS;
+       return mtd->_write_oob(mtd, to, ops);
 }
 
-static inline int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
-                                        size_t len)
-{
-       if (!mtd->lock_user_prot_reg)
-               return -EOPNOTSUPP;
-       return mtd->lock_user_prot_reg(mtd, from, len);
-}
+int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+                          size_t len);
+int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+                          size_t *retlen, u_char *buf);
+int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+                          size_t len);
+int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+                          size_t *retlen, u_char *buf);
+int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
+                           size_t *retlen, u_char *buf);
+int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len);
 
 int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
               unsigned long count, loff_t to, size_t *retlen);
 
 static inline void mtd_sync(struct mtd_info *mtd)
 {
-       if (mtd->sync)
-               mtd->sync(mtd);
-}
-
-/* Chip-supported device locking */
-static inline int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
-{
-       if (!mtd->lock)
-               return -EOPNOTSUPP;
-       return mtd->lock(mtd, ofs, len);
+       if (mtd->_sync)
+               mtd->_sync(mtd);
 }
 
-static inline int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
-{
-       if (!mtd->unlock)
-               return -EOPNOTSUPP;
-       return mtd->unlock(mtd, ofs, len);
-}
-
-static inline int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
-{
-       if (!mtd->is_locked)
-               return -EOPNOTSUPP;
-       return mtd->is_locked(mtd, ofs, len);
-}
+int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs);
+int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs);
 
 static inline int mtd_suspend(struct mtd_info *mtd)
 {
-       return mtd->suspend ? mtd->suspend(mtd) : 0;
+       return mtd->_suspend ? mtd->_suspend(mtd) : 0;
 }
 
 static inline void mtd_resume(struct mtd_info *mtd)
 {
-       if (mtd->resume)
-               mtd->resume(mtd);
-}
-
-static inline int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
-{
-       if (!mtd->block_isbad)
-               return 0;
-       return mtd->block_isbad(mtd, ofs);
-}
-
-static inline int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
-       if (!mtd->block_markbad)
-               return -EOPNOTSUPP;
-       return mtd->block_markbad(mtd, ofs);
+       if (mtd->_resume)
+               mtd->_resume(mtd);
 }
 
 static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
@@ -482,12 +346,12 @@ static inline uint32_t mtd_mod_by_ws(uint64_t sz, struct mtd_info *mtd)
 
 static inline int mtd_has_oob(const struct mtd_info *mtd)
 {
-       return mtd->read_oob && mtd->write_oob;
+       return mtd->_read_oob && mtd->_write_oob;
 }
 
 static inline int mtd_can_have_bb(const struct mtd_info *mtd)
 {
-       return !!mtd->block_isbad;
+       return !!mtd->_block_isbad;
 }
 
        /* Kernel-side ioctl definitions */
index 63b5a8b6dfbda263525aada5aeee309b312b2620..1482340d3d9f5e0a6ba5d61a020b112320618362 100644 (file)
@@ -324,6 +324,7 @@ struct nand_hw_control {
  * @steps:     number of ECC steps per page
  * @size:      data bytes per ECC step
  * @bytes:     ECC bytes per step
+ * @strength:  max number of correctible bits per ECC step
  * @total:     total number of ECC bytes per page
  * @prepad:    padding information for syndrome based ECC generators
  * @postpad:   padding information for syndrome based ECC generators
@@ -351,6 +352,7 @@ struct nand_ecc_ctrl {
        int size;
        int bytes;
        int total;
+       int strength;
        int prepad;
        int postpad;
        struct nand_ecclayout   *layout;
@@ -448,8 +450,9 @@ struct nand_buffers {
  *                     will be copied to the appropriate nand_bbt_descr's.
  * @badblockpos:       [INTERN] position of the bad block marker in the oob
  *                     area.
- * @badblockbits:      [INTERN] number of bits to left-shift the bad block
- *                     number
+ * @badblockbits:      [INTERN] minimum number of set bits in a good block's
+ *                     bad block marker position; i.e., BBM == 11110111b is
+ *                     not bad when badblockbits == 7
  * @cellinfo:          [INTERN] MLC/multichip data from chip ident
  * @numchips:          [INTERN] number of physical chips
  * @chipsize:          [INTERN] the size of one chip for multichip arrays
diff --git a/include/linux/mtd/pmc551.h b/include/linux/mtd/pmc551.h
deleted file mode 100644 (file)
index 27ad40a..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * PMC551 PCI Mezzanine Ram Device
- *
- * Author:
- *       Mark Ferrell
- *       Copyright 1999,2000 Nortel Networks
- *
- * License:
- *      As part of this driver was derrived from the slram.c driver it falls
- *      under the same license, which is GNU General Public License v2
- */
-
-#ifndef __MTD_PMC551_H__
-#define __MTD_PMC551_H__
-
-#include <linux/mtd/mtd.h>
-
-#define PMC551_VERSION \
-       "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n"
-
-/*
- * Our personal and private information
- */
-struct mypriv {
-        struct pci_dev *dev;
-        u_char *start;
-        u32    base_map0;
-        u32    curr_map0;
-        u32    asize;
-       struct mtd_info *nextpmc551;
-};
-
-/*
- * Function Prototypes
- */
-static int pmc551_erase(struct mtd_info *, struct erase_info *);
-static void pmc551_unpoint(struct mtd_info *, loff_t, size_t);
-static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
-               size_t *retlen, void **virt, resource_size_t *phys);
-static int pmc551_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
-static int pmc551_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
-
-
-/*
- * Define the PCI ID's if the kernel doesn't define them for us
- */
-#ifndef PCI_VENDOR_ID_V3_SEMI
-#define PCI_VENDOR_ID_V3_SEMI             0x11b0
-#endif
-
-#ifndef PCI_DEVICE_ID_V3_SEMI_V370PDC
-#define PCI_DEVICE_ID_V3_SEMI_V370PDC     0x0200
-#endif
-
-
-#define PMC551_PCI_MEM_MAP0    0x50
-#define PMC551_PCI_MEM_MAP1    0x54
-#define PMC551_PCI_MEM_MAP_MAP_ADDR_MASK       0x3ff00000
-#define PMC551_PCI_MEM_MAP_APERTURE_MASK       0x000000f0
-#define PMC551_PCI_MEM_MAP_REG_EN              0x00000002
-#define PMC551_PCI_MEM_MAP_ENABLE              0x00000001
-
-#define PMC551_SDRAM_MA                0x60
-#define PMC551_SDRAM_CMD       0x62
-#define PMC551_DRAM_CFG                0x64
-#define PMC551_SYS_CTRL_REG    0x78
-
-#define PMC551_DRAM_BLK0       0x68
-#define PMC551_DRAM_BLK1       0x6c
-#define PMC551_DRAM_BLK2       0x70
-#define PMC551_DRAM_BLK3       0x74
-#define PMC551_DRAM_BLK_GET_SIZE(x) (524288<<((x>>4)&0x0f))
-#define PMC551_DRAM_BLK_SET_COL_MUX(x,v) (((x) & ~0x00007000) | (((v) & 0x7) << 12))
-#define PMC551_DRAM_BLK_SET_ROW_MUX(x,v) (((x) & ~0x00000f00) | (((v) & 0xf) << 8))
-
-
-#endif /* __MTD_PMC551_H__ */
-
index 9cf4c4c7955509a98590df62e13dcb4f679ac346..a38e1fa8af0194d5e2ac43be29f3a65a313afd76 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
+#include <linux/pm_qos.h>
 
 /* FLCTL registers */
 #define FLCMNCR(f)             (f->reg + 0x0)
@@ -38,6 +39,7 @@
 #define FLDTFIFO(f)            (f->reg + 0x24)
 #define FLECFIFO(f)            (f->reg + 0x28)
 #define FLTRCR(f)              (f->reg + 0x2C)
+#define FLHOLDCR(f)            (f->reg + 0x38)
 #define        FL4ECCRESULT0(f)        (f->reg + 0x80)
 #define        FL4ECCRESULT1(f)        (f->reg + 0x84)
 #define        FL4ECCRESULT2(f)        (f->reg + 0x88)
 #define        CE0_ENABLE      (0x1 << 3)      /* Chip Enable 0 */
 #define        TYPESEL_SET     (0x1 << 0)
 
+/*
+ * Clock settings using the PULSEx registers from FLCMNCR
+ *
+ * Some hardware uses bits called PULSEx instead of FCKSEL_E and QTSEL_E
+ * to control the clock divider used between the High-Speed Peripheral Clock
+ * and the FLCTL internal clock. If so, use CLK_8_BIT_xxx for connecting 8 bit
+ * and CLK_16_BIT_xxx for connecting 16 bit bus bandwith NAND chips. For the 16
+ * bit version the divider is seperate for the pulse width of high and low
+ * signals.
+ */
+#define PULSE3 (0x1 << 27)
+#define PULSE2 (0x1 << 17)
+#define PULSE1 (0x1 << 15)
+#define PULSE0 (0x1 << 9)
+#define CLK_8B_0_5                     PULSE1
+#define CLK_8B_1                       0x0
+#define CLK_8B_1_5                     (PULSE1 | PULSE2)
+#define CLK_8B_2                       PULSE0
+#define CLK_8B_3                       (PULSE0 | PULSE1 | PULSE2)
+#define CLK_8B_4                       (PULSE0 | PULSE2)
+#define CLK_16B_6L_2H                  PULSE0
+#define CLK_16B_9L_3H                  (PULSE0 | PULSE1 | PULSE2)
+#define CLK_16B_12L_4H                 (PULSE0 | PULSE2)
+
 /* FLCMDCR control bits */
 #define ADRCNT2_E      (0x1 << 31)     /* 5byte address enable */
 #define ADRMD_E                (0x1 << 26)     /* Sector address access */
 #define TRSTRT         (0x1 << 0)      /* translation start */
 #define TREND          (0x1 << 1)      /* translation end */
 
+/*
+ * FLHOLDCR control bits
+ *
+ * HOLDEN: Bus Occupancy Enable (inverted)
+ * Enable this bit when the external bus might be used in between transfers.
+ * If not set and the bus gets used by other modules, a deadlock occurs.
+ */
+#define HOLDEN         (0x1 << 0)
+
 /* FL4ECCCR control bits */
 #define        _4ECCFA         (0x1 << 2)      /* 4 symbols correct fault */
 #define        _4ECCEND        (0x1 << 1)      /* 4 symbols end */
@@ -97,6 +132,7 @@ struct sh_flctl {
        struct mtd_info         mtd;
        struct nand_chip        chip;
        struct platform_device  *pdev;
+       struct dev_pm_qos_request pm_qos;
        void __iomem            *reg;
 
        uint8_t done_buff[2048 + 64];   /* max size 2048 + 64 */
@@ -108,11 +144,14 @@ struct sh_flctl {
        int     erase1_page_addr;       /* page_addr in ERASE1 cmd */
        uint32_t erase_ADRCNT;          /* bits of FLCMDCR in ERASE1 cmd */
        uint32_t rw_ADRCNT;     /* bits of FLCMDCR in READ WRITE cmd */
+       uint32_t flcmncr_base;  /* base value of FLCMNCR */
 
        int     hwecc_cant_correct[4];
 
        unsigned page_size:1;   /* NAND page size (0 = 512, 1 = 2048) */
        unsigned hwecc:1;       /* Hardware ECC (0 = disabled, 1 = enabled) */
+       unsigned holden:1;      /* Hardware has FLHOLDCR and HOLDEN is set */
+       unsigned qos_request:1; /* QoS request to prevent deep power shutdown */
 };
 
 struct sh_flctl_platform_data {
@@ -121,6 +160,7 @@ struct sh_flctl_platform_data {
        unsigned long           flcmncr_val;
 
        unsigned has_hwecc:1;
+       unsigned use_holden:1;
 };
 
 static inline struct sh_flctl *mtd_to_flctl(struct mtd_info *mtdinfo)
diff --git a/include/linux/mtd/spear_smi.h b/include/linux/mtd/spear_smi.h
new file mode 100644 (file)
index 0000000..8ae1726
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright Â© 2010 ST Microelectronics
+ * Shiraz Hashim <shiraz.hashim@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MTD_SPEAR_SMI_H
+#define __MTD_SPEAR_SMI_H
+
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+/* max possible slots for serial-nor flash chip in the SMI controller */
+#define MAX_NUM_FLASH_CHIP     4
+
+/* macro to define partitions for flash devices */
+#define DEFINE_PARTS(n, of, s)         \
+{                                      \
+       .name = n,                      \
+       .offset = of,                   \
+       .size = s,                      \
+}
+
+/**
+ * struct spear_smi_flash_info - platform structure for passing flash
+ * information
+ *
+ * name: name of the serial nor flash for identification
+ * mem_base: the memory base on which the flash is mapped
+ * size: size of the flash in bytes
+ * partitions: parition details
+ * nr_partitions: number of partitions
+ * fast_mode: whether flash supports fast mode
+ */
+
+struct spear_smi_flash_info {
+       char *name;
+       unsigned long mem_base;
+       unsigned long size;
+       struct mtd_partition *partitions;
+       int nr_partitions;
+       u8 fast_mode;
+};
+
+/**
+ * struct spear_smi_plat_data - platform structure for configuring smi
+ *
+ * clk_rate: clk rate at which SMI must operate
+ * num_flashes: number of flashes present on board
+ * board_flash_info: specific details of each flash present on board
+ */
+struct spear_smi_plat_data {
+       unsigned long clk_rate;
+       int num_flashes;
+       struct spear_smi_flash_info *board_flash_info;
+       struct device_node *np[MAX_NUM_FLASH_CHIP];
+};
+
+#endif /* __MTD_SPEAR_SMI_H */
index 8f825756c459b6d53afda01394aa7bb4e67406c2..18543e2db06f202e0c9c2b6dc1d3e6fae1c07fb5 100644 (file)
@@ -194,6 +194,7 @@ struct      mtpos {
 #define MT_ST_SYSV              0x1000
 #define MT_ST_NOWAIT            0x2000
 #define MT_ST_SILI             0x4000
+#define MT_ST_NOWAIT_EOF       0x8000
 
 /* The mode parameters to be controlled. Parameter chosen with bits 20-28 */
 #define MT_ST_CLEAR_DEFAULT    0xfffff
index bd9f55a5958d4cd982190292076123fcb3fd6ea6..ddbb6a901f653b7880ed293ee2f67db17a01b850 100644 (file)
@@ -299,18 +299,31 @@ struct perf_event_mmap_page {
        /*
         * Bits needed to read the hw events in user-space.
         *
-        *   u32 seq;
-        *   s64 count;
+        *   u32 seq, time_mult, time_shift, idx, width;
+        *   u64 count, enabled, running;
+        *   u64 cyc, time_offset;
+        *   s64 pmc = 0;
         *
         *   do {
         *     seq = pc->lock;
-        *
         *     barrier()
-        *     if (pc->index) {
-        *       count = pmc_read(pc->index - 1);
-        *       count += pc->offset;
-        *     } else
-        *       goto regular_read;
+        *
+        *     enabled = pc->time_enabled;
+        *     running = pc->time_running;
+        *
+        *     if (pc->cap_usr_time && enabled != running) {
+        *       cyc = rdtsc();
+        *       time_offset = pc->time_offset;
+        *       time_mult   = pc->time_mult;
+        *       time_shift  = pc->time_shift;
+        *     }
+        *
+        *     idx = pc->index;
+        *     count = pc->offset;
+        *     if (pc->cap_usr_rdpmc && idx) {
+        *       width = pc->pmc_width;
+        *       pmc = rdpmc(idx - 1);
+        *     }
         *
         *     barrier();
         *   } while (pc->lock != seq);
@@ -323,14 +336,57 @@ struct perf_event_mmap_page {
        __s64   offset;                 /* add to hardware event value */
        __u64   time_enabled;           /* time event active */
        __u64   time_running;           /* time event on cpu */
-       __u32   time_mult, time_shift;
+       union {
+               __u64   capabilities;
+               __u64   cap_usr_time  : 1,
+                       cap_usr_rdpmc : 1,
+                       cap_____res   : 62;
+       };
+
+       /*
+        * If cap_usr_rdpmc this field provides the bit-width of the value
+        * read using the rdpmc() or equivalent instruction. This can be used
+        * to sign extend the result like:
+        *
+        *   pmc <<= 64 - width;
+        *   pmc >>= 64 - width; // signed shift right
+        *   count += pmc;
+        */
+       __u16   pmc_width;
+
+       /*
+        * If cap_usr_time the below fields can be used to compute the time
+        * delta since time_enabled (in ns) using rdtsc or similar.
+        *
+        *   u64 quot, rem;
+        *   u64 delta;
+        *
+        *   quot = (cyc >> time_shift);
+        *   rem = cyc & ((1 << time_shift) - 1);
+        *   delta = time_offset + quot * time_mult +
+        *              ((rem * time_mult) >> time_shift);
+        *
+        * Where time_offset,time_mult,time_shift and cyc are read in the
+        * seqcount loop described above. This delta can then be added to
+        * enabled and possible running (if idx), improving the scaling:
+        *
+        *   enabled += delta;
+        *   if (idx)
+        *     running += delta;
+        *
+        *   quot = count / running;
+        *   rem  = count % running;
+        *   count = quot * enabled + (rem * enabled) / running;
+        */
+       __u16   time_shift;
+       __u32   time_mult;
        __u64   time_offset;
 
                /*
                 * Hole for extension of the self monitor capabilities
                 */
 
-       __u64   __reserved[121];        /* align to 1k */
+       __u64   __reserved[120];        /* align to 1k */
 
        /*
         * Control data for the mmap() data buffer.
@@ -550,6 +606,7 @@ struct perf_guest_info_callbacks {
 #include <linux/irq_work.h>
 #include <linux/static_key.h>
 #include <linux/atomic.h>
+#include <linux/sysfs.h>
 #include <asm/local.h>
 
 #define PERF_MAX_STACK_DEPTH           255
@@ -1291,5 +1348,18 @@ do {                                                                     \
        register_cpu_notifier(&fn##_nb);                                \
 } while (0)
 
+
+#define PMU_FORMAT_ATTR(_name, _format)                                        \
+static ssize_t                                                         \
+_name##_show(struct device *dev,                                       \
+                              struct device_attribute *attr,           \
+                              char *page)                              \
+{                                                                      \
+       BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE);                     \
+       return sprintf(page, _format "\n");                             \
+}                                                                      \
+                                                                       \
+static struct device_attribute format_attr_##_name = __ATTR_RO(_name)
+
 #endif /* __KERNEL__ */
 #endif /* _LINUX_PERF_EVENT_H */
diff --git a/include/linux/platform_data/spear_thermal.h b/include/linux/platform_data/spear_thermal.h
new file mode 100644 (file)
index 0000000..724f2e1
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * SPEAr thermal driver platform data.
+ *
+ * Copyright (C) 2011-2012 ST Microelectronics
+ * Author: Vincenzo Frascino <vincenzo.frascino@st.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef SPEAR_THERMAL_H
+#define SPEAR_THERMAL_H
+
+/* SPEAr Thermal Sensor Platform Data */
+struct spear_thermal_pdata {
+       /* flags used to enable thermal sensor */
+       unsigned int thermal_flags;
+};
+
+#endif /* SPEAR_THERMAL_H */
index fe99211fb2b8c99cac624244b02d9b819ce3a9d7..e01b167e66f068223f86321109b77687ce5ef50c 100644 (file)
@@ -27,6 +27,8 @@
 #define MAX17042_BATTERY_FULL  (100)
 #define MAX17042_DEFAULT_SNS_RESISTOR  (10000)
 
+#define MAX17042_CHARACTERIZATION_DATA_SIZE 48
+
 enum max17042_register {
        MAX17042_STATUS         = 0x00,
        MAX17042_VALRT_Th       = 0x01,
@@ -40,11 +42,11 @@ enum max17042_register {
        MAX17042_VCELL          = 0x09,
        MAX17042_Current        = 0x0A,
        MAX17042_AvgCurrent     = 0x0B,
-       MAX17042_Qresidual      = 0x0C,
+
        MAX17042_SOC            = 0x0D,
        MAX17042_AvSOC          = 0x0E,
        MAX17042_RemCap         = 0x0F,
-       MAX17402_FullCAP        = 0x10,
+       MAX17042_FullCAP        = 0x10,
        MAX17042_TTE            = 0x11,
        MAX17042_V_empty        = 0x12,
 
@@ -62,14 +64,14 @@ enum max17042_register {
        MAX17042_AvCap          = 0x1F,
        MAX17042_ManName        = 0x20,
        MAX17042_DevName        = 0x21,
-       MAX17042_DevChem        = 0x22,
 
+       MAX17042_FullCAPNom     = 0x23,
        MAX17042_TempNom        = 0x24,
-       MAX17042_TempCold       = 0x25,
+       MAX17042_TempLim        = 0x25,
        MAX17042_TempHot        = 0x26,
        MAX17042_AIN            = 0x27,
        MAX17042_LearnCFG       = 0x28,
-       MAX17042_SHFTCFG        = 0x29,
+       MAX17042_FilterCFG      = 0x29,
        MAX17042_RelaxCFG       = 0x2A,
        MAX17042_MiscCFG        = 0x2B,
        MAX17042_TGAIN          = 0x2C,
@@ -77,22 +79,41 @@ enum max17042_register {
        MAX17042_CGAIN          = 0x2E,
        MAX17042_COFF           = 0x2F,
 
-       MAX17042_Q_empty        = 0x33,
+       MAX17042_MaskSOC        = 0x32,
+       MAX17042_SOC_empty      = 0x33,
        MAX17042_T_empty        = 0x34,
 
+       MAX17042_FullCAP0       = 0x35,
+       MAX17042_LAvg_empty     = 0x36,
+       MAX17042_FCTC           = 0x37,
        MAX17042_RCOMP0         = 0x38,
        MAX17042_TempCo         = 0x39,
-       MAX17042_Rx             = 0x3A,
-       MAX17042_T_empty0       = 0x3B,
+       MAX17042_EmptyTempCo    = 0x3A,
+       MAX17042_K_empty0       = 0x3B,
        MAX17042_TaskPeriod     = 0x3C,
        MAX17042_FSTAT          = 0x3D,
 
        MAX17042_SHDNTIMER      = 0x3F,
 
-       MAX17042_VFRemCap       = 0x4A,
+       MAX17042_dQacc          = 0x45,
+       MAX17042_dPacc          = 0x46,
+
+       MAX17042_VFSOC0         = 0x48,
 
        MAX17042_QH             = 0x4D,
        MAX17042_QL             = 0x4E,
+
+       MAX17042_VFSOC0Enable   = 0x60,
+       MAX17042_MLOCKReg1      = 0x62,
+       MAX17042_MLOCKReg2      = 0x63,
+
+       MAX17042_MODELChrTbl    = 0x80,
+
+       MAX17042_OCV            = 0xEE,
+
+       MAX17042_OCVInternal    = 0xFB,
+
+       MAX17042_VFSOC          = 0xFF,
 };
 
 /*
@@ -105,10 +126,64 @@ struct max17042_reg_data {
        u16 data;
 };
 
+struct max17042_config_data {
+       /* External current sense resistor value in milli-ohms */
+       u32     cur_sense_val;
+
+       /* A/D measurement */
+       u16     tgain;          /* 0x2C */
+       u16     toff;           /* 0x2D */
+       u16     cgain;          /* 0x2E */
+       u16     coff;           /* 0x2F */
+
+       /* Alert / Status */
+       u16     valrt_thresh;   /* 0x01 */
+       u16     talrt_thresh;   /* 0x02 */
+       u16     soc_alrt_thresh;        /* 0x03 */
+       u16     config;         /* 0x01D */
+       u16     shdntimer;      /* 0x03F */
+
+       /* App data */
+       u16     design_cap;     /* 0x18 */
+       u16     ichgt_term;     /* 0x1E */
+
+       /* MG3 config */
+       u16     at_rate;        /* 0x04 */
+       u16     learn_cfg;      /* 0x28 */
+       u16     filter_cfg;     /* 0x29 */
+       u16     relax_cfg;      /* 0x2A */
+       u16     misc_cfg;       /* 0x2B */
+       u16     masksoc;        /* 0x32 */
+
+       /* MG3 save and restore */
+       u16     fullcap;        /* 0x10 */
+       u16     fullcapnom;     /* 0x23 */
+       u16     socempty;       /* 0x33 */
+       u16     lavg_empty;     /* 0x36 */
+       u16     dqacc;          /* 0x45 */
+       u16     dpacc;          /* 0x46 */
+
+       /* Cell technology from power_supply.h */
+       u16     cell_technology;
+
+       /* Cell Data */
+       u16     vempty;         /* 0x12 */
+       u16     temp_nom;       /* 0x24 */
+       u16     temp_lim;       /* 0x25 */
+       u16     fctc;           /* 0x37 */
+       u16     rcomp0;         /* 0x38 */
+       u16     tcompc0;        /* 0x39 */
+       u16     empty_tempco;   /* 0x3A */
+       u16     kempty0;        /* 0x3B */
+       u16     cell_char_tbl[MAX17042_CHARACTERIZATION_DATA_SIZE];
+} __packed;
+
 struct max17042_platform_data {
        struct max17042_reg_data *init_data;
+       struct max17042_config_data *config_data;
        int num_init_data; /* Number of enties in init_data array */
        bool enable_current_sense;
+       bool enable_por_init; /* Use POR init from Maxim appnote */
 
        /*
         * R_sns in micro-ohms.
diff --git a/include/linux/power/smb347-charger.h b/include/linux/power/smb347-charger.h
new file mode 100644 (file)
index 0000000..b3cb20d
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Summit Microelectronics SMB347 Battery Charger Driver
+ *
+ * Copyright (C) 2011, Intel Corporation
+ *
+ * Authors: Bruce E. Robertson <bruce.e.robertson@intel.com>
+ *          Mika Westerberg <mika.westerberg@linux.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.
+ */
+
+#ifndef SMB347_CHARGER_H
+#define SMB347_CHARGER_H
+
+#include <linux/types.h>
+#include <linux/power_supply.h>
+
+enum {
+       /* use the default compensation method */
+       SMB347_SOFT_TEMP_COMPENSATE_DEFAULT = -1,
+
+       SMB347_SOFT_TEMP_COMPENSATE_NONE,
+       SMB347_SOFT_TEMP_COMPENSATE_CURRENT,
+       SMB347_SOFT_TEMP_COMPENSATE_VOLTAGE,
+};
+
+/* Use default factory programmed value for hard/soft temperature limit */
+#define SMB347_TEMP_USE_DEFAULT                -273
+
+/*
+ * Charging enable can be controlled by software (via i2c) by
+ * smb347-charger driver or by EN pin (active low/high).
+ */
+enum smb347_chg_enable {
+       SMB347_CHG_ENABLE_SW,
+       SMB347_CHG_ENABLE_PIN_ACTIVE_LOW,
+       SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH,
+};
+
+/**
+ * struct smb347_charger_platform_data - platform data for SMB347 charger
+ * @battery_info: Information about the battery
+ * @max_charge_current: maximum current (in uA) the battery can be charged
+ * @max_charge_voltage: maximum voltage (in uV) the battery can be charged
+ * @pre_charge_current: current (in uA) to use in pre-charging phase
+ * @termination_current: current (in uA) used to determine when the
+ *                      charging cycle terminates
+ * @pre_to_fast_voltage: voltage (in uV) treshold used for transitioning to
+ *                      pre-charge to fast charge mode
+ * @mains_current_limit: maximum input current drawn from AC/DC input (in uA)
+ * @usb_hc_current_limit: maximum input high current (in uA) drawn from USB
+ *                       input
+ * @chip_temp_threshold: die temperature where device starts limiting charge
+ *                      current [%100 - %130] (in degree C)
+ * @soft_cold_temp_limit: soft cold temperature limit [%0 - %15] (in degree C),
+ *                       granularity is 5 deg C.
+ * @soft_hot_temp_limit: soft hot temperature limit [%40 - %55] (in degree  C),
+ *                      granularity is 5 deg C.
+ * @hard_cold_temp_limit: hard cold temperature limit [%-5 - %10] (in degree C),
+ *                       granularity is 5 deg C.
+ * @hard_hot_temp_limit: hard hot temperature limit [%50 - %65] (in degree C),
+ *                      granularity is 5 deg C.
+ * @suspend_on_hard_temp_limit: suspend charging when hard limit is hit
+ * @soft_temp_limit_compensation: compensation method when soft temperature
+ *                               limit is hit
+ * @charge_current_compensation: current (in uA) for charging compensation
+ *                              current when temperature hits soft limits
+ * @use_mains: AC/DC input can be used
+ * @use_usb: USB input can be used
+ * @use_usb_otg: USB OTG output can be used (not implemented yet)
+ * @irq_gpio: GPIO number used for interrupts (%-1 if not used)
+ * @enable_control: how charging enable/disable is controlled
+ *                 (driver/pin controls)
+ *
+ * @use_main, @use_usb, and @use_usb_otg are means to enable/disable
+ * hardware support for these. This is useful when we want to have for
+ * example OTG charging controlled via OTG transceiver driver and not by
+ * the SMB347 hardware.
+ *
+ * Hard and soft temperature limit values are given as described in the
+ * device data sheet and assuming NTC beta value is %3750. Even if this is
+ * not the case, these values should be used. They can be mapped to the
+ * corresponding NTC beta values with the help of table %2 in the data
+ * sheet. So for example if NTC beta is %3375 and we want to program hard
+ * hot limit to be %53 deg C, @hard_hot_temp_limit should be set to %50.
+ *
+ * If zero value is given in any of the current and voltage values, the
+ * factory programmed default will be used. For soft/hard temperature
+ * values, pass in %SMB347_TEMP_USE_DEFAULT instead.
+ */
+struct smb347_charger_platform_data {
+       struct power_supply_info battery_info;
+       unsigned int    max_charge_current;
+       unsigned int    max_charge_voltage;
+       unsigned int    pre_charge_current;
+       unsigned int    termination_current;
+       unsigned int    pre_to_fast_voltage;
+       unsigned int    mains_current_limit;
+       unsigned int    usb_hc_current_limit;
+       unsigned int    chip_temp_threshold;
+       int             soft_cold_temp_limit;
+       int             soft_hot_temp_limit;
+       int             hard_cold_temp_limit;
+       int             hard_hot_temp_limit;
+       bool            suspend_on_hard_temp_limit;
+       unsigned int    soft_temp_limit_compensation;
+       unsigned int    charge_current_compensation;
+       bool            use_mains;
+       bool            use_usb;
+       bool            use_usb_otg;
+       int             irq_gpio;
+       enum smb347_chg_enable enable_control;
+};
+
+#endif /* SMB347_CHARGER_H */
index 67be0376d8e35e0593945dd9df507ed8528d02ce..7be2e88f23fdae28f322f484c36a409e00c39eb3 100644 (file)
@@ -151,6 +151,9 @@ int ring_buffer_empty_cpu(struct ring_buffer *buffer, int cpu);
 
 void ring_buffer_record_disable(struct ring_buffer *buffer);
 void ring_buffer_record_enable(struct ring_buffer *buffer);
+void ring_buffer_record_off(struct ring_buffer *buffer);
+void ring_buffer_record_on(struct ring_buffer *buffer);
+int ring_buffer_record_is_on(struct ring_buffer *buffer);
 void ring_buffer_record_disable_cpu(struct ring_buffer *buffer, int cpu);
 void ring_buffer_record_enable_cpu(struct ring_buffer *buffer, int cpu);
 
index 1dba6ee55203fec99992d211eb169938fcec918e..c75128bed5fa39116952fd148ff01374993b8f44 100644 (file)
@@ -143,7 +143,6 @@ static inline int tboot_enabled(void)
 
 extern void tboot_probe(void);
 extern void tboot_shutdown(u32 shutdown_type);
-extern void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control);
 extern struct acpi_table_header *tboot_get_dmar_table(
                                      struct acpi_table_header *dmar_tbl);
 extern int tboot_force_iommu(void);
index 9c23ee8fd2d33d037cb7cb5760dc4130948b1ccc..917741bb8e11d18b1dc7c88ddbbadc850029223e 100644 (file)
@@ -261,7 +261,8 @@ struct iscsi_uevent {
                } host_event;
                struct msg_ping_comp {
                        uint32_t        host_no;
-                       uint32_t        status;
+                       uint32_t        status; /* enum
+                                                * iscsi_ping_status_code */
                        uint32_t        pid;    /* unique ping id associated
                                                   with each ping request */
                        uint32_t        data_size;
@@ -483,6 +484,20 @@ enum iscsi_port_state {
        ISCSI_PORT_STATE_UP             = 0x2,
 };
 
+/* iSCSI PING status/error code */
+enum iscsi_ping_status_code {
+       ISCSI_PING_SUCCESS                      = 0,
+       ISCSI_PING_FW_DISABLED                  = 0x1,
+       ISCSI_PING_IPADDR_INVALID               = 0x2,
+       ISCSI_PING_LINKLOCAL_IPV6_ADDR_INVALID  = 0x3,
+       ISCSI_PING_TIMEOUT                      = 0x4,
+       ISCSI_PING_INVALID_DEST_ADDR            = 0x5,
+       ISCSI_PING_OVERSIZE_PACKET              = 0x6,
+       ISCSI_PING_ICMP_ERROR                   = 0x7,
+       ISCSI_PING_MAX_REQ_EXCEEDED             = 0x8,
+       ISCSI_PING_NO_ARP_RECEIVED              = 0x9,
+};
+
 #define iscsi_ptr(_handle) ((void*)(unsigned long)_handle)
 #define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr)
 
@@ -578,6 +593,6 @@ struct iscsi_chap_rec {
        char username[ISCSI_CHAP_AUTH_NAME_MAX_LEN];
        uint8_t password[ISCSI_CHAP_AUTH_SECRET_MAX_LEN];
        uint8_t password_length;
-} __packed;
+};
 
 #endif
index 5a35a2a2d3c514bef92ef7bed3d32216cc08c206..cfdb55f0937e37002d21be6eaf9f81833683aa4b 100644 (file)
@@ -165,7 +165,8 @@ struct fcoe_ctlr {
  * @switch_name: WWN of switch from advertisement
  * @fabric_name: WWN of fabric from advertisement
  * @fc_map:     FC_MAP value from advertisement
- * @fcf_mac:    Ethernet address of the FCF
+ * @fcf_mac:    Ethernet address of the FCF for FIP traffic
+ * @fcoe_mac:   Ethernet address of the FCF for FCoE traffic
  * @vfid:       virtual fabric ID
  * @pri:        selection priority, smaller values are better
  * @flogi_sent:         current FLOGI sent to this FCF
@@ -188,6 +189,7 @@ struct fcoe_fcf {
        u32 fc_map;
        u16 vfid;
        u8 fcf_mac[ETH_ALEN];
+       u8 fcoe_mac[ETH_ALEN];
 
        u8 pri;
        u8 flogi_sent;
index 84f3001a568d9edf435c0465b7d421bbc74e858c..91b91e8056737878e71897b9e9e96d000b722d52 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/writeback.h>
 #include <linux/tracepoint.h>
+#include <trace/events/gfpflags.h>
 
 struct btrfs_root;
 struct btrfs_fs_info;
@@ -862,6 +863,49 @@ TRACE_EVENT(btrfs_setup_cluster,
                  __entry->size, __entry->max_size, __entry->bitmap)
 );
 
+struct extent_state;
+TRACE_EVENT(alloc_extent_state,
+
+       TP_PROTO(struct extent_state *state, gfp_t mask, unsigned long IP),
+
+       TP_ARGS(state, mask, IP),
+
+       TP_STRUCT__entry(
+               __field(struct extent_state *, state)
+               __field(gfp_t, mask)
+               __field(unsigned long, ip)
+       ),
+
+       TP_fast_assign(
+               __entry->state  = state,
+               __entry->mask   = mask,
+               __entry->ip     = IP
+       ),
+
+       TP_printk("state=%p; mask = %s; caller = %pF", __entry->state,
+                 show_gfp_flags(__entry->mask), (void *)__entry->ip)
+);
+
+TRACE_EVENT(free_extent_state,
+
+       TP_PROTO(struct extent_state *state, unsigned long IP),
+
+       TP_ARGS(state, IP),
+
+       TP_STRUCT__entry(
+               __field(struct extent_state *, state)
+               __field(unsigned long, ip)
+       ),
+
+       TP_fast_assign(
+               __entry->state  = state,
+               __entry->ip = IP
+       ),
+
+       TP_printk(" state=%p; caller = %pF", __entry->state,
+                 (void *)__entry->ip)
+);
+
 #endif /* _TRACE_BTRFS_H */
 
 /* This part must be outside protection */
index f4ea4b6f3cf1eae8725ab6107f20bab58324f103..ed64ccac67c9707d0b4310227eb84d820710fbec 100644 (file)
@@ -1883,7 +1883,7 @@ static void cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp,
  */
 int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
 {
-       int retval;
+       int retval = 0;
        struct cgroup_subsys *ss, *failed_ss = NULL;
        struct cgroup *oldcgrp;
        struct cgroupfs_root *root = cgrp->root;
index 4b50357914fb437a30cd146e1bd33e1f2b43c449..a6a9ec4cd8f583d640ab0da9e4941b19fe8fd990 100644 (file)
@@ -3348,7 +3348,7 @@ static void calc_timer_values(struct perf_event *event,
        *running = ctx_time - event->tstamp_running;
 }
 
-void __weak perf_update_user_clock(struct perf_event_mmap_page *userpg, u64 now)
+void __weak arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now)
 {
 }
 
@@ -3398,7 +3398,7 @@ void perf_event_update_userpage(struct perf_event *event)
        userpg->time_running = running +
                        atomic64_read(&event->child_total_time_running);
 
-       perf_update_user_clock(userpg, now);
+       arch_perf_update_userpage(userpg, now);
 
        barrier();
        ++userpg->lock;
@@ -7116,6 +7116,13 @@ void __init perf_event_init(void)
 
        /* do not patch jump label more than once per second */
        jump_label_rate_limit(&perf_sched_events, HZ);
+
+       /*
+        * Build time assertion that we keep the data_head at the intended
+        * location.  IOW, validation we got the __reserved[] size right.
+        */
+       BUILD_BUG_ON((offsetof(struct perf_event_mmap_page, data_head))
+                    != 1024);
 }
 
 static int __init perf_event_sysfs_init(void)
index 72efa1e4359af576a9cb96ed0ffb40461bd6ef1a..e2b0fb9a0b3b3d0d5871ee37bf08618a6bf052e7 100644 (file)
@@ -59,6 +59,7 @@
 #include <linux/magic.h>
 #include <linux/pid.h>
 #include <linux/nsproxy.h>
+#include <linux/ptrace.h>
 
 #include <asm/futex.h>
 
@@ -2443,40 +2444,31 @@ SYSCALL_DEFINE3(get_robust_list, int, pid,
 {
        struct robust_list_head __user *head;
        unsigned long ret;
-       const struct cred *cred = current_cred(), *pcred;
+       struct task_struct *p;
 
        if (!futex_cmpxchg_enabled)
                return -ENOSYS;
 
+       WARN_ONCE(1, "deprecated: get_robust_list will be deleted in 2013.\n");
+
+       rcu_read_lock();
+
+       ret = -ESRCH;
        if (!pid)
-               head = current->robust_list;
+               p = current;
        else {
-               struct task_struct *p;
-
-               ret = -ESRCH;
-               rcu_read_lock();
                p = find_task_by_vpid(pid);
                if (!p)
                        goto err_unlock;
-               ret = -EPERM;
-               pcred = __task_cred(p);
-               /* If victim is in different user_ns, then uids are not
-                  comparable, so we must have CAP_SYS_PTRACE */
-               if (cred->user->user_ns != pcred->user->user_ns) {
-                       if (!ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE))
-                               goto err_unlock;
-                       goto ok;
-               }
-               /* If victim is in same user_ns, then uids are comparable */
-               if (cred->euid != pcred->euid &&
-                   cred->euid != pcred->uid &&
-                   !ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE))
-                       goto err_unlock;
-ok:
-               head = p->robust_list;
-               rcu_read_unlock();
        }
 
+       ret = -EPERM;
+       if (!ptrace_may_access(p, PTRACE_MODE_READ))
+               goto err_unlock;
+
+       head = p->robust_list;
+       rcu_read_unlock();
+
        if (put_user(sizeof(*head), len_ptr))
                return -EFAULT;
        return put_user(head, head_ptr);
index 5f9e689dc8f0f7d52824108c56712e45e2e81560..83e368b005fc6eb001a2babe7a48d80dcb21037e 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/compat.h>
 #include <linux/nsproxy.h>
 #include <linux/futex.h>
+#include <linux/ptrace.h>
 
 #include <asm/uaccess.h>
 
@@ -136,40 +137,31 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
 {
        struct compat_robust_list_head __user *head;
        unsigned long ret;
-       const struct cred *cred = current_cred(), *pcred;
+       struct task_struct *p;
 
        if (!futex_cmpxchg_enabled)
                return -ENOSYS;
 
+       WARN_ONCE(1, "deprecated: get_robust_list will be deleted in 2013.\n");
+
+       rcu_read_lock();
+
+       ret = -ESRCH;
        if (!pid)
-               head = current->compat_robust_list;
+               p = current;
        else {
-               struct task_struct *p;
-
-               ret = -ESRCH;
-               rcu_read_lock();
                p = find_task_by_vpid(pid);
                if (!p)
                        goto err_unlock;
-               ret = -EPERM;
-               pcred = __task_cred(p);
-               /* If victim is in different user_ns, then uids are not
-                  comparable, so we must have CAP_SYS_PTRACE */
-               if (cred->user->user_ns != pcred->user->user_ns) {
-                       if (!ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE))
-                               goto err_unlock;
-                       goto ok;
-               }
-               /* If victim is in same user_ns, then uids are comparable */
-               if (cred->euid != pcred->euid &&
-                   cred->euid != pcred->uid &&
-                   !ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE))
-                       goto err_unlock;
-ok:
-               head = p->compat_robust_list;
-               rcu_read_unlock();
        }
 
+       ret = -EPERM;
+       if (!ptrace_may_access(p, PTRACE_MODE_READ))
+               goto err_unlock;
+
+       head = p->compat_robust_list;
+       rcu_read_unlock();
+
        if (put_user(sizeof(*head), len_ptr))
                return -EFAULT;
        return put_user(ptr_to_compat(head), head_ptr);
index 6ff84e6a954c052ed1f7f483559728a6154647d6..bdb1803255513ee8ee53c3683541348c6c9a4e0b 100644 (file)
@@ -54,14 +54,18 @@ static void warn_no_thread(unsigned int irq, struct irqaction *action)
 static void irq_wake_thread(struct irq_desc *desc, struct irqaction *action)
 {
        /*
-        * Wake up the handler thread for this action. In case the
-        * thread crashed and was killed we just pretend that we
-        * handled the interrupt. The hardirq handler has disabled the
-        * device interrupt, so no irq storm is lurking. If the
+        * In case the thread crashed and was killed we just pretend that
+        * we handled the interrupt. The hardirq handler has disabled the
+        * device interrupt, so no irq storm is lurking.
+        */
+       if (action->thread->flags & PF_EXITING)
+               return;
+
+       /*
+        * Wake up the handler thread for this action. If the
         * RUNTHREAD bit is already set, nothing to do.
         */
-       if ((action->thread->flags & PF_EXITING) ||
-           test_and_set_bit(IRQTF_RUNTHREAD, &action->thread_flags))
+       if (test_and_set_bit(IRQTF_RUNTHREAD, &action->thread_flags))
                return;
 
        /*
index b0ccd1ac2d6a4259ef7647b95669cd90252665f2..89a3ea82569b00ac69da1bec696a90bce878790a 100644 (file)
@@ -282,7 +282,7 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)
 {
        struct irq_chip *chip = irq_desc_get_chip(desc);
        struct cpumask *set = irq_default_affinity;
-       int ret;
+       int ret, node = desc->irq_data.node;
 
        /* Excludes PER_CPU and NO_BALANCE interrupts */
        if (!irq_can_set_affinity(irq))
@@ -301,6 +301,13 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)
        }
 
        cpumask_and(mask, cpu_online_mask, set);
+       if (node != NUMA_NO_NODE) {
+               const struct cpumask *nodemask = cpumask_of_node(node);
+
+               /* make sure at least one of the cpus in nodemask is online */
+               if (cpumask_intersects(mask, nodemask))
+                       cpumask_and(mask, mask, nodemask);
+       }
        ret = chip->irq_set_affinity(&desc->irq_data, mask, false);
        switch (ret) {
        case IRQ_SET_MASK_OK:
@@ -645,7 +652,7 @@ static int irq_wait_for_interrupt(struct irqaction *action)
  * is marked MASKED.
  */
 static void irq_finalize_oneshot(struct irq_desc *desc,
-                                struct irqaction *action, bool force)
+                                struct irqaction *action)
 {
        if (!(desc->istate & IRQS_ONESHOT))
                return;
@@ -679,7 +686,7 @@ again:
         * we would clear the threads_oneshot bit of this thread which
         * was just set.
         */
-       if (!force && test_bit(IRQTF_RUNTHREAD, &action->thread_flags))
+       if (test_bit(IRQTF_RUNTHREAD, &action->thread_flags))
                goto out_unlock;
 
        desc->threads_oneshot &= ~action->thread_mask;
@@ -739,7 +746,7 @@ irq_forced_thread_fn(struct irq_desc *desc, struct irqaction *action)
 
        local_bh_disable();
        ret = action->thread_fn(action->irq, action->dev_id);
-       irq_finalize_oneshot(desc, action, false);
+       irq_finalize_oneshot(desc, action);
        local_bh_enable();
        return ret;
 }
@@ -755,7 +762,7 @@ static irqreturn_t irq_thread_fn(struct irq_desc *desc,
        irqreturn_t ret;
 
        ret = action->thread_fn(action->irq, action->dev_id);
-       irq_finalize_oneshot(desc, action, false);
+       irq_finalize_oneshot(desc, action);
        return ret;
 }
 
@@ -844,7 +851,7 @@ void exit_irq_thread(void)
                wake_threads_waitq(desc);
 
        /* Prevent a stale desc->threads_oneshot */
-       irq_finalize_oneshot(desc, action, true);
+       irq_finalize_oneshot(desc, action);
 }
 
 static void irq_setup_forced_threading(struct irqaction *new)
index 47420908fba0a97df65676862df84fd63fdeb839..c3c89751b327c9cf257c870d046973107be810da 100644 (file)
@@ -43,12 +43,16 @@ void irq_move_masked_irq(struct irq_data *idata)
         * masking the irqs.
         */
        if (likely(cpumask_any_and(desc->pending_mask, cpu_online_mask)
-                  < nr_cpu_ids))
-               if (!chip->irq_set_affinity(&desc->irq_data,
-                                           desc->pending_mask, false)) {
+                  < nr_cpu_ids)) {
+               int ret = chip->irq_set_affinity(&desc->irq_data,
+                                                desc->pending_mask, false);
+               switch (ret) {
+               case IRQ_SET_MASK_OK:
                        cpumask_copy(desc->irq_data.affinity, desc->pending_mask);
+               case IRQ_SET_MASK_OK_NOCOPY:
                        irq_set_thread_affinity(desc);
                }
+       }
 
        cpumask_clear(desc->pending_mask);
 }
index e3ed0ecee7c7a47c2b8c85e3717b3606343c30aa..4603b9d8f30a362d15dc60e4bd0cd659fd7c3f89 100644 (file)
@@ -1270,7 +1270,7 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
        int dest_cpu;
 
        /* Look for allowed, online CPU in same node. */
-       for_each_cpu_mask(dest_cpu, *nodemask) {
+       for_each_cpu(dest_cpu, nodemask) {
                if (!cpu_online(dest_cpu))
                        continue;
                if (!cpu_active(dest_cpu))
@@ -1281,7 +1281,7 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
 
        for (;;) {
                /* Any allowed, online CPU? */
-               for_each_cpu_mask(dest_cpu, *tsk_cpus_allowed(p)) {
+               for_each_cpu(dest_cpu, tsk_cpus_allowed(p)) {
                        if (!cpu_online(dest_cpu))
                                continue;
                        if (!cpu_active(dest_cpu))
@@ -1964,6 +1964,7 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
        local_irq_enable();
 #endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */
        finish_lock_switch(rq, prev);
+       finish_arch_post_lock_switch();
 
        fire_sched_in_preempt_notifiers(current);
        if (mm)
@@ -3101,8 +3102,6 @@ EXPORT_SYMBOL(sub_preempt_count);
  */
 static noinline void __schedule_bug(struct task_struct *prev)
 {
-       struct pt_regs *regs = get_irq_regs();
-
        if (oops_in_progress)
                return;
 
@@ -3113,11 +3112,7 @@ static noinline void __schedule_bug(struct task_struct *prev)
        print_modules();
        if (irqs_disabled())
                print_irqtrace_events(prev);
-
-       if (regs)
-               show_regs(regs);
-       else
-               dump_stack();
+       dump_stack();
 }
 
 /*
index 42b1f304b0447512eb1f3fb808477d004ac5c03a..fb3acba4d52e052c8bf6a5da32979313a88e0478 100644 (file)
@@ -681,6 +681,9 @@ static inline int task_running(struct rq *rq, struct task_struct *p)
 #ifndef finish_arch_switch
 # define finish_arch_switch(prev)      do { } while (0)
 #endif
+#ifndef finish_arch_post_lock_switch
+# define finish_arch_post_lock_switch()        do { } while (0)
+#endif
 
 #ifndef __ARCH_WANT_UNLOCKED_CTXSW
 static inline void prepare_lock_switch(struct rq *rq, struct task_struct *next)
index cd3134510f3d0b9d807a532d6d9ab271e98a40f1..a1d2849f247314ce7fc5d33e51ff3c06d3641771 100644 (file)
@@ -141,7 +141,7 @@ if FTRACE
 config FUNCTION_TRACER
        bool "Kernel Function Tracer"
        depends on HAVE_FUNCTION_TRACER
-       select FRAME_POINTER if !ARM_UNWIND && !S390 && !MICROBLAZE
+       select FRAME_POINTER if !ARM_UNWIND && !PPC && !S390 && !MICROBLAZE
        select KALLSYMS
        select GENERIC_TRACER
        select CONTEXT_SWITCH_TRACER
index 867bd1dd2dd09250f3a52663844ee1b54435bbd3..0fa92f677c9209e2ed046752752de986e34f071e 100644 (file)
@@ -249,7 +249,8 @@ static void update_ftrace_function(void)
 #else
        __ftrace_trace_function = func;
 #endif
-       ftrace_trace_function = ftrace_test_stop_func;
+       ftrace_trace_function =
+               (func == ftrace_stub) ? func : ftrace_test_stop_func;
 #endif
 }
 
index f5b7b5c1195beaf806f19d59e0ce7114c0e36add..cf8d11e91efdf92d95dad58d6fa771d2ac998786 100644 (file)
@@ -154,33 +154,10 @@ enum {
 
 static unsigned long ring_buffer_flags __read_mostly = RB_BUFFERS_ON;
 
-#define BUF_PAGE_HDR_SIZE offsetof(struct buffer_data_page, data)
-
-/**
- * tracing_on - enable all tracing buffers
- *
- * This function enables all tracing buffers that may have been
- * disabled with tracing_off.
- */
-void tracing_on(void)
-{
-       set_bit(RB_BUFFERS_ON_BIT, &ring_buffer_flags);
-}
-EXPORT_SYMBOL_GPL(tracing_on);
+/* Used for individual buffers (after the counter) */
+#define RB_BUFFER_OFF          (1 << 20)
 
-/**
- * tracing_off - turn off all tracing buffers
- *
- * This function stops all tracing buffers from recording data.
- * It does not disable any overhead the tracers themselves may
- * be causing. This function simply causes all recording to
- * the ring buffers to fail.
- */
-void tracing_off(void)
-{
-       clear_bit(RB_BUFFERS_ON_BIT, &ring_buffer_flags);
-}
-EXPORT_SYMBOL_GPL(tracing_off);
+#define BUF_PAGE_HDR_SIZE offsetof(struct buffer_data_page, data)
 
 /**
  * tracing_off_permanent - permanently disable ring buffers
@@ -193,15 +170,6 @@ void tracing_off_permanent(void)
        set_bit(RB_BUFFERS_DISABLED_BIT, &ring_buffer_flags);
 }
 
-/**
- * tracing_is_on - show state of ring buffers enabled
- */
-int tracing_is_on(void)
-{
-       return ring_buffer_flags == RB_BUFFERS_ON;
-}
-EXPORT_SYMBOL_GPL(tracing_is_on);
-
 #define RB_EVNT_HDR_SIZE (offsetof(struct ring_buffer_event, array))
 #define RB_ALIGNMENT           4U
 #define RB_MAX_SMALL_DATA      (RB_ALIGNMENT * RINGBUF_TYPE_DATA_TYPE_LEN_MAX)
@@ -2618,6 +2586,63 @@ void ring_buffer_record_enable(struct ring_buffer *buffer)
 }
 EXPORT_SYMBOL_GPL(ring_buffer_record_enable);
 
+/**
+ * ring_buffer_record_off - stop all writes into the buffer
+ * @buffer: The ring buffer to stop writes to.
+ *
+ * This prevents all writes to the buffer. Any attempt to write
+ * to the buffer after this will fail and return NULL.
+ *
+ * This is different than ring_buffer_record_disable() as
+ * it works like an on/off switch, where as the disable() verison
+ * must be paired with a enable().
+ */
+void ring_buffer_record_off(struct ring_buffer *buffer)
+{
+       unsigned int rd;
+       unsigned int new_rd;
+
+       do {
+               rd = atomic_read(&buffer->record_disabled);
+               new_rd = rd | RB_BUFFER_OFF;
+       } while (atomic_cmpxchg(&buffer->record_disabled, rd, new_rd) != rd);
+}
+EXPORT_SYMBOL_GPL(ring_buffer_record_off);
+
+/**
+ * ring_buffer_record_on - restart writes into the buffer
+ * @buffer: The ring buffer to start writes to.
+ *
+ * This enables all writes to the buffer that was disabled by
+ * ring_buffer_record_off().
+ *
+ * This is different than ring_buffer_record_enable() as
+ * it works like an on/off switch, where as the enable() verison
+ * must be paired with a disable().
+ */
+void ring_buffer_record_on(struct ring_buffer *buffer)
+{
+       unsigned int rd;
+       unsigned int new_rd;
+
+       do {
+               rd = atomic_read(&buffer->record_disabled);
+               new_rd = rd & ~RB_BUFFER_OFF;
+       } while (atomic_cmpxchg(&buffer->record_disabled, rd, new_rd) != rd);
+}
+EXPORT_SYMBOL_GPL(ring_buffer_record_on);
+
+/**
+ * ring_buffer_record_is_on - return true if the ring buffer can write
+ * @buffer: The ring buffer to see if write is enabled
+ *
+ * Returns true if the ring buffer is in a state that it accepts writes.
+ */
+int ring_buffer_record_is_on(struct ring_buffer *buffer)
+{
+       return !atomic_read(&buffer->record_disabled);
+}
+
 /**
  * ring_buffer_record_disable_cpu - stop all writes into the cpu_buffer
  * @buffer: The ring buffer to stop writes to.
@@ -4039,68 +4064,6 @@ int ring_buffer_read_page(struct ring_buffer *buffer,
 }
 EXPORT_SYMBOL_GPL(ring_buffer_read_page);
 
-#ifdef CONFIG_TRACING
-static ssize_t
-rb_simple_read(struct file *filp, char __user *ubuf,
-              size_t cnt, loff_t *ppos)
-{
-       unsigned long *p = filp->private_data;
-       char buf[64];
-       int r;
-
-       if (test_bit(RB_BUFFERS_DISABLED_BIT, p))
-               r = sprintf(buf, "permanently disabled\n");
-       else
-               r = sprintf(buf, "%d\n", test_bit(RB_BUFFERS_ON_BIT, p));
-
-       return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
-}
-
-static ssize_t
-rb_simple_write(struct file *filp, const char __user *ubuf,
-               size_t cnt, loff_t *ppos)
-{
-       unsigned long *p = filp->private_data;
-       unsigned long val;
-       int ret;
-
-       ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
-       if (ret)
-               return ret;
-
-       if (val)
-               set_bit(RB_BUFFERS_ON_BIT, p);
-       else
-               clear_bit(RB_BUFFERS_ON_BIT, p);
-
-       (*ppos)++;
-
-       return cnt;
-}
-
-static const struct file_operations rb_simple_fops = {
-       .open           = tracing_open_generic,
-       .read           = rb_simple_read,
-       .write          = rb_simple_write,
-       .llseek         = default_llseek,
-};
-
-
-static __init int rb_init_debugfs(void)
-{
-       struct dentry *d_tracer;
-
-       d_tracer = tracing_init_dentry();
-
-       trace_create_file("tracing_on", 0644, d_tracer,
-                           &ring_buffer_flags, &rb_simple_fops);
-
-       return 0;
-}
-
-fs_initcall(rb_init_debugfs);
-#endif
-
 #ifdef CONFIG_HOTPLUG_CPU
 static int rb_cpu_notify(struct notifier_block *self,
                         unsigned long action, void *hcpu)
index 10d5503f0d04d1782a98888ba8e31e05b096516c..ed7b5d1e12f468168178b1a3c144d9736e0b4614 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/ctype.h>
 #include <linux/init.h>
 #include <linux/poll.h>
+#include <linux/nmi.h>
 #include <linux/fs.h>
 
 #include "trace.h"
@@ -351,6 +352,59 @@ static void wakeup_work_handler(struct work_struct *work)
 
 static DECLARE_DELAYED_WORK(wakeup_work, wakeup_work_handler);
 
+/**
+ * tracing_on - enable tracing buffers
+ *
+ * This function enables tracing buffers that may have been
+ * disabled with tracing_off.
+ */
+void tracing_on(void)
+{
+       if (global_trace.buffer)
+               ring_buffer_record_on(global_trace.buffer);
+       /*
+        * This flag is only looked at when buffers haven't been
+        * allocated yet. We don't really care about the race
+        * between setting this flag and actually turning
+        * on the buffer.
+        */
+       global_trace.buffer_disabled = 0;
+}
+EXPORT_SYMBOL_GPL(tracing_on);
+
+/**
+ * tracing_off - turn off tracing buffers
+ *
+ * This function stops the tracing buffers from recording data.
+ * It does not disable any overhead the tracers themselves may
+ * be causing. This function simply causes all recording to
+ * the ring buffers to fail.
+ */
+void tracing_off(void)
+{
+       if (global_trace.buffer)
+               ring_buffer_record_on(global_trace.buffer);
+       /*
+        * This flag is only looked at when buffers haven't been
+        * allocated yet. We don't really care about the race
+        * between setting this flag and actually turning
+        * on the buffer.
+        */
+       global_trace.buffer_disabled = 1;
+}
+EXPORT_SYMBOL_GPL(tracing_off);
+
+/**
+ * tracing_is_on - show state of ring buffers enabled
+ */
+int tracing_is_on(void)
+{
+       if (global_trace.buffer)
+               return ring_buffer_record_is_on(global_trace.buffer);
+       return !global_trace.buffer_disabled;
+}
+EXPORT_SYMBOL_GPL(tracing_is_on);
+
 /**
  * trace_wake_up - wake up tasks waiting for trace input
  *
@@ -1644,6 +1698,7 @@ __find_next_entry(struct trace_iterator *iter, int *ent_cpu,
        int cpu_file = iter->cpu_file;
        u64 next_ts = 0, ts;
        int next_cpu = -1;
+       int next_size = 0;
        int cpu;
 
        /*
@@ -1675,9 +1730,12 @@ __find_next_entry(struct trace_iterator *iter, int *ent_cpu,
                        next_cpu = cpu;
                        next_ts = ts;
                        next_lost = lost_events;
+                       next_size = iter->ent_size;
                }
        }
 
+       iter->ent_size = next_size;
+
        if (ent_cpu)
                *ent_cpu = next_cpu;
 
@@ -4567,6 +4625,55 @@ static __init void create_trace_options_dir(void)
                create_trace_option_core_file(trace_options[i], i);
 }
 
+static ssize_t
+rb_simple_read(struct file *filp, char __user *ubuf,
+              size_t cnt, loff_t *ppos)
+{
+       struct ring_buffer *buffer = filp->private_data;
+       char buf[64];
+       int r;
+
+       if (buffer)
+               r = ring_buffer_record_is_on(buffer);
+       else
+               r = 0;
+
+       r = sprintf(buf, "%d\n", r);
+
+       return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static ssize_t
+rb_simple_write(struct file *filp, const char __user *ubuf,
+               size_t cnt, loff_t *ppos)
+{
+       struct ring_buffer *buffer = filp->private_data;
+       unsigned long val;
+       int ret;
+
+       ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
+       if (ret)
+               return ret;
+
+       if (buffer) {
+               if (val)
+                       ring_buffer_record_on(buffer);
+               else
+                       ring_buffer_record_off(buffer);
+       }
+
+       (*ppos)++;
+
+       return cnt;
+}
+
+static const struct file_operations rb_simple_fops = {
+       .open           = tracing_open_generic,
+       .read           = rb_simple_read,
+       .write          = rb_simple_write,
+       .llseek         = default_llseek,
+};
+
 static __init int tracer_init_debugfs(void)
 {
        struct dentry *d_tracer;
@@ -4626,6 +4733,9 @@ static __init int tracer_init_debugfs(void)
        trace_create_file("trace_clock", 0644, d_tracer, NULL,
                          &trace_clock_fops);
 
+       trace_create_file("tracing_on", 0644, d_tracer,
+                           global_trace.buffer, &rb_simple_fops);
+
 #ifdef CONFIG_DYNAMIC_FTRACE
        trace_create_file("dyn_ftrace_total_info", 0444, d_tracer,
                        &ftrace_update_tot_cnt, &tracing_dyn_info_fops);
@@ -4798,6 +4908,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
                        if (ret != TRACE_TYPE_NO_CONSUME)
                                trace_consume(&iter);
                }
+               touch_nmi_watchdog();
 
                trace_printk_seq(&iter.seq);
        }
@@ -4863,6 +4974,8 @@ __init static int tracer_alloc_buffers(void)
                goto out_free_cpumask;
        }
        global_trace.entries = ring_buffer_size(global_trace.buffer);
+       if (global_trace.buffer_disabled)
+               tracing_off();
 
 
 #ifdef CONFIG_TRACER_MAX_TRACE
index 54faec790bc18c53ddcc40df65640d340d2f01d3..95059f091a242abcfd60bbe9169131e7870e4a2c 100644 (file)
@@ -154,6 +154,7 @@ struct trace_array {
        struct ring_buffer      *buffer;
        unsigned long           entries;
        int                     cpu;
+       int                     buffer_disabled;
        cycle_t                 time_start;
        struct task_struct      *waiter;
        struct trace_array_cpu  *data[NR_CPUS];
@@ -835,13 +836,11 @@ extern const char *__stop___trace_bprintk_fmt[];
                     filter)
 #include "trace_entries.h"
 
-#ifdef CONFIG_PERF_EVENTS
 #ifdef CONFIG_FUNCTION_TRACER
 int perf_ftrace_event_register(struct ftrace_event_call *call,
                               enum trace_reg type, void *data);
 #else
 #define perf_ftrace_event_register NULL
 #endif /* CONFIG_FUNCTION_TRACER */
-#endif /* CONFIG_PERF_EVENTS */
 
 #endif /* _LINUX_KERNEL_TRACE_H */
index d91eb0541b3aa73f206e7f6ec8eee4c563c04233..4108e1250ca2c2b83e58a99c851c6506fe1348bb 100644 (file)
@@ -166,6 +166,12 @@ FTRACE_ENTRY_DUP(wakeup, ctx_switch_entry,
 
 #define FTRACE_STACK_ENTRIES   8
 
+#ifndef CONFIG_64BIT
+# define IP_FMT "%08lx"
+#else
+# define IP_FMT "%016lx"
+#endif
+
 FTRACE_ENTRY(kernel_stack, stack_entry,
 
        TRACE_STACK,
@@ -175,8 +181,9 @@ FTRACE_ENTRY(kernel_stack, stack_entry,
                __dynamic_array(unsigned long,  caller  )
        ),
 
-       F_printk("\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n"
-                "\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n",
+       F_printk("\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n"
+                "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n"
+                "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n",
                 __entry->caller[0], __entry->caller[1], __entry->caller[2],
                 __entry->caller[3], __entry->caller[4], __entry->caller[5],
                 __entry->caller[6], __entry->caller[7]),
@@ -193,8 +200,9 @@ FTRACE_ENTRY(user_stack, userstack_entry,
                __array(        unsigned long,  caller, FTRACE_STACK_ENTRIES    )
        ),
 
-       F_printk("\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n"
-                "\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n",
+       F_printk("\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n"
+                "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n"
+                "\t=> (" IP_FMT ")\n\t=> (" IP_FMT ")\n",
                 __entry->caller[0], __entry->caller[1], __entry->caller[2],
                 __entry->caller[3], __entry->caller[4], __entry->caller[5],
                 __entry->caller[6], __entry->caller[7]),
index 7b46c9bd22aef8ed4f6de572965c439af54b5ff8..3dd15e8bc856d87f7e4525a1577b5af579db6f96 100644 (file)
@@ -162,7 +162,7 @@ ftrace_define_fields_##name(struct ftrace_event_call *event_call)   \
 #define __dynamic_array(type, item)
 
 #undef F_printk
-#define F_printk(fmt, args...) #fmt ", "  __stringify(args)
+#define F_printk(fmt, args...) __stringify(fmt) ", "  __stringify(args)
 
 #undef FTRACE_ENTRY_REG
 #define FTRACE_ENTRY_REG(call, struct_name, etype, tstruct, print, filter,\
index d897278b1f975f0c4a3af72ec32406056fc76bc9..6a3ee981931d3c2b4438cff1d220ec2e8039bd7e 100644 (file)
@@ -104,7 +104,7 @@ as-option = $(call try-run,\
 # Usage: cflags-y += $(call as-instr,instr,option1,option2)
 
 as-instr = $(call try-run,\
-       /bin/echo -e "$(1)" | $(CC) $(KBUILD_AFLAGS) -c -xassembler -o "$$TMP" -,$(2),$(3))
+       printf "%b\n" "$(1)" | $(CC) $(KBUILD_AFLAGS) -c -xassembler -o "$$TMP" -,$(2),$(3))
 
 # cc-option
 # Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586)
index d2b366c16b6482cc90ba8f262d79d1ddbb2c87b2..ff1720d28d0c80be0df2e1d0ae8f5697794e36e9 100644 (file)
@@ -69,6 +69,7 @@ warning-1 += -Wmissing-prototypes
 warning-1 += -Wold-style-definition
 warning-1 += $(call cc-option, -Wmissing-include-dirs)
 warning-1 += $(call cc-option, -Wunused-but-set-variable)
+warning-1 += $(call cc-disable-warning, missing-field-initializers)
 
 warning-2 := -Waggregate-return
 warning-2 += -Wcast-align
@@ -76,6 +77,7 @@ warning-2 += -Wdisabled-optimization
 warning-2 += -Wnested-externs
 warning-2 += -Wshadow
 warning-2 += $(call cc-option, -Wlogical-op)
+warning-2 += $(call cc-option, -Wmissing-field-initializers)
 
 warning-3 := -Wbad-function-cast
 warning-3 += -Wcast-qual
index 00c368c6e996fb0555f8161b6d3147c554d037e5..0be6f110cce79be4c4acd2f4dbf4ac2ce7d24057 100644 (file)
@@ -304,6 +304,30 @@ cmd_lzo = (cat $(filter-out FORCE,$^) | \
        lzop -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ || \
        (rm -f $@ ; false)
 
+# U-Boot mkimage
+# ---------------------------------------------------------------------------
+
+MKIMAGE := $(srctree)/scripts/mkuboot.sh
+
+# SRCARCH just happens to match slightly more than ARCH (on sparc), so reduces
+# the number of overrides in arch makefiles
+UIMAGE_ARCH ?= $(SRCARCH)
+UIMAGE_COMPRESSION ?= $(if $(2),$(2),none)
+UIMAGE_OPTS-y ?=
+UIMAGE_TYPE ?= kernel
+UIMAGE_LOADADDR ?= arch_must_set_this
+UIMAGE_ENTRYADDR ?= $(UIMAGE_LOADADDR)
+UIMAGE_NAME ?= 'Linux-$(KERNELRELEASE)'
+UIMAGE_IN ?= $<
+UIMAGE_OUT ?= $@
+
+quiet_cmd_uimage = UIMAGE  $(UIMAGE_OUT)
+      cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A $(UIMAGE_ARCH) -O linux \
+                       -C $(UIMAGE_COMPRESSION) $(UIMAGE_OPTS-y) \
+                       -T $(UIMAGE_TYPE) \
+                       -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \
+                       -n $(UIMAGE_NAME) -d $(UIMAGE_IN) $(UIMAGE_OUT)
+
 # XZ
 # ---------------------------------------------------------------------------
 # Use xzkern to compress the kernel image and xzmisc to compress other things.
diff --git a/scripts/coccinelle/api/ptr_ret.cocci b/scripts/coccinelle/api/ptr_ret.cocci
new file mode 100644 (file)
index 0000000..cbfd08c
--- /dev/null
@@ -0,0 +1,70 @@
+///
+/// Use PTR_RET rather than if(IS_ERR(...)) + PTR_ERR
+///
+// Confidence: High
+// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6.  GPLv2.
+// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6.  GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Options: -no_includes -include_headers
+//
+// Keywords: ERR_PTR, PTR_ERR, PTR_RET
+// Version min: 2.6.39
+//
+
+virtual context
+virtual patch
+virtual org
+virtual report
+
+@depends on patch@
+expression ptr;
+@@
+
+- if (IS_ERR(ptr)) return PTR_ERR(ptr); else return 0;
++ return PTR_RET(ptr);
+
+@depends on patch@
+expression ptr;
+@@
+
+- if (IS_ERR(ptr)) return PTR_ERR(ptr); return 0;
++ return PTR_RET(ptr);
+
+@r1 depends on !patch@
+expression ptr;
+position p1;
+@@
+
+* if@p1 (IS_ERR(ptr)) return PTR_ERR(ptr); else return 0;
+
+@r2 depends on !patch@
+expression ptr;
+position p2;
+@@
+
+* if@p2 (IS_ERR(ptr)) return PTR_ERR(ptr); return 0;
+
+@script:python depends on org@
+p << r1.p1;
+@@
+
+coccilib.org.print_todo(p[0], "WARNING: PTR_RET can be used")
+
+
+@script:python depends on org@
+p << r2.p2;
+@@
+
+coccilib.org.print_todo(p[0], "WARNING: PTR_RET can be used")
+
+@script:python depends on report@
+p << r1.p1;
+@@
+
+coccilib.report.print_report(p[0], "WARNING: PTR_RET can be used")
+
+@script:python depends on report@
+p << r2.p2;
+@@
+
+coccilib.report.print_report(p[0], "WARNING: PTR_RET can be used")
diff --git a/scripts/coccinelle/free/clk_put.cocci b/scripts/coccinelle/free/clk_put.cocci
new file mode 100644 (file)
index 0000000..46747ad
--- /dev/null
@@ -0,0 +1,67 @@
+/// Find missing clk_puts.
+///
+//# This only signals a missing clk_put when there is a clk_put later
+//# in the same function.
+//# False positives can be due to loops.
+//
+// Confidence: Moderate
+// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6.  GPLv2.
+// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6.  GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments:
+// Options:
+
+virtual context
+virtual org
+virtual report
+
+@clk@
+expression e;
+statement S,S1;
+int ret;
+position p1,p2,p3;
+@@
+
+e = clk_get@p1(...)
+... when != clk_put(e)
+if (<+...e...+>) S
+... when any
+    when != clk_put(e)
+    when != if (...) { ... clk_put(e); ... }
+(
+ if (ret == 0) S1
+|
+if (...)
+   { ...
+     return 0; }
+|
+if (...)
+   { ...
+     return <+...e...+>; }
+|
+*if@p2 (...)
+   { ... when != clk_put(e)
+         when forall
+     return@p3 ...; }
+)
+... when any
+clk_put(e);
+
+@script:python depends on org@
+p1 << clk.p1;
+p2 << clk.p2;
+p3 << clk.p3;
+@@
+
+cocci.print_main("clk_get",p1)
+cocci.print_secs("if",p2)
+cocci.print_secs("needed clk_put",p3)
+
+@script:python depends on report@
+p1 << clk.p1;
+p2 << clk.p2;
+p3 << clk.p3;
+@@
+
+msg = "ERROR: missing clk_put; clk_get on line %s and execution via conditional on line %s" % (p1[0].line,p2[0].line)
+coccilib.report.print_report(p3[0],msg)
diff --git a/scripts/coccinelle/free/iounmap.cocci b/scripts/coccinelle/free/iounmap.cocci
new file mode 100644 (file)
index 0000000..5384f4b
--- /dev/null
@@ -0,0 +1,67 @@
+/// Find missing iounmaps.
+///
+//# This only signals a missing iounmap when there is an iounmap later
+//# in the same function.
+//# False positives can be due to loops.
+//
+// Confidence: Moderate
+// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6.  GPLv2.
+// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6.  GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments:
+// Options:
+
+virtual context
+virtual org
+virtual report
+
+@iom@
+expression e;
+statement S,S1;
+int ret;
+position p1,p2,p3;
+@@
+
+e = \(ioremap@p1\|ioremap_nocache@p1\)(...)
+... when != iounmap(e)
+if (<+...e...+>) S
+... when any
+    when != iounmap(e)
+    when != if (...) { ... iounmap(e); ... }
+(
+ if (ret == 0) S1
+|
+if (...)
+   { ...
+     return 0; }
+|
+if (...)
+   { ...
+     return <+...e...+>; }
+|
+*if@p2 (...)
+   { ... when != iounmap(e)
+         when forall
+     return@p3 ...; }
+)
+... when any
+iounmap(e);
+
+@script:python depends on org@
+p1 << iom.p1;
+p2 << iom.p2;
+p3 << iom.p3;
+@@
+
+cocci.print_main("ioremap",p1)
+cocci.print_secs("if",p2)
+cocci.print_secs("needed iounmap",p3)
+
+@script:python depends on report@
+p1 << iom.p1;
+p2 << iom.p2;
+p3 << iom.p3;
+@@
+
+msg = "ERROR: missing iounmap; ioremap on line %s and execution via conditional on line %s" % (p1[0].line,p2[0].line)
+coccilib.report.print_report(p3[0],msg)
diff --git a/scripts/coccinelle/misc/boolinit.cocci b/scripts/coccinelle/misc/boolinit.cocci
new file mode 100644 (file)
index 0000000..97ce41c
--- /dev/null
@@ -0,0 +1,178 @@
+/// Bool initializations should use true and false.  Bool tests don't need
+/// comparisons.  Based on contributions from Joe Perches, Rusty Russell
+/// and Bruce W Allan.
+///
+// Confidence: High
+// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6.  GPLv2.
+// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6.  GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Options: -include_headers
+
+virtual patch
+virtual context
+virtual org
+virtual report
+
+@depends on patch@
+bool t;
+symbol true;
+symbol false;
+@@
+
+(
+- t == true
++ t
+|
+- true == t
++ t
+|
+- t != true
++ !t
+|
+- true != t
++ !t
+|
+- t == false
++ !t
+|
+- false == t
++ !t
+|
+- t != false
++ t
+|
+- false != t
++ t
+)
+
+@depends on patch disable is_zero, isnt_zero@
+bool t;
+@@
+
+(
+- t == 1
++ t
+|
+- t != 1
++ !t
+|
+- t == 0
++ !t
+|
+- t != 0
++ t
+)
+
+@depends on patch@
+bool b;
+@@
+(
+ b =
+- 0
++ false
+|
+ b =
+- 1
++ true
+)
+
+// ---------------------------------------------------------------------
+
+@r1 depends on !patch@
+bool t;
+position p;
+@@
+
+(
+* t@p == true
+|
+* true == t@p
+|
+* t@p != true
+|
+* true != t@p
+|
+* t@p == false
+|
+* false == t@p
+|
+* t@p != false
+|
+* false != t@p
+)
+
+@r2 depends on !patch disable is_zero, isnt_zero@
+bool t;
+position p;
+@@
+
+(
+* t@p == 1
+|
+* t@p != 1
+|
+* t@p == 0
+|
+* t@p != 0
+)
+
+@r3 depends on !patch@
+bool b;
+position p1,p2;
+constant c;
+@@
+(
+*b@p1 = 0
+|
+*b@p1 = 1
+|
+*b@p2 = c
+)
+
+@script:python depends on org@
+p << r1.p;
+@@
+
+cocci.print_main("WARNING: Comparison to bool",p)
+
+@script:python depends on org@
+p << r2.p;
+@@
+
+cocci.print_main("WARNING: Comparison of bool to 0/1",p)
+
+@script:python depends on org@
+p1 << r3.p1;
+@@
+
+cocci.print_main("WARNING: Assignment of bool to 0/1",p1)
+
+@script:python depends on org@
+p2 << r3.p2;
+@@
+
+cocci.print_main("ERROR: Assignment of bool to non-0/1 constant",p2)
+
+@script:python depends on report@
+p << r1.p;
+@@
+
+coccilib.report.print_report(p[0],"WARNING: Comparison to bool")
+
+@script:python depends on report@
+p << r2.p;
+@@
+
+coccilib.report.print_report(p[0],"WARNING: Comparison of bool to 0/1")
+
+@script:python depends on report@
+p1 << r3.p1;
+@@
+
+coccilib.report.print_report(p1[0],"WARNING: Assignment of bool to 0/1")
+
+@script:python depends on report@
+p2 << r3.p2;
+@@
+
+coccilib.report.print_report(p2[0],"ERROR: Assignment of bool to non-0/1 constant")
diff --git a/scripts/coccinelle/misc/cstptr.cocci b/scripts/coccinelle/misc/cstptr.cocci
new file mode 100644 (file)
index 0000000..d425644
--- /dev/null
@@ -0,0 +1,41 @@
+/// PTR_ERR should be applied before its argument is reassigned, typically
+/// to NULL
+///
+// Confidence: High
+// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6.  GPLv2.
+// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6.  GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments:
+// Options: -no_includes -include_headers
+
+virtual org
+virtual report
+virtual context
+
+@r exists@
+expression e,e1;
+constant c;
+position p1,p2;
+@@
+
+*e@p1 = c
+... when != e = e1
+    when != &e
+    when != true IS_ERR(e)
+*PTR_ERR@p2(e)
+
+@script:python depends on org@
+p1 << r.p1;
+p2 << r.p2;
+@@
+
+cocci.print_main("PTR_ERR",p2)
+cocci.print_secs("assignment",p1)
+
+@script:python depends on report@
+p1 << r.p1;
+p2 << r.p2;
+@@
+
+msg = "ERROR: PTR_ERR applied after initialization to constant on line %s" % (p1[0].line)
+coccilib.report.print_report(p2[0],msg)
diff --git a/scripts/coccinelle/null/badzero.cocci b/scripts/coccinelle/null/badzero.cocci
new file mode 100644 (file)
index 0000000..d79baf7
--- /dev/null
@@ -0,0 +1,237 @@
+/// Compare pointer-typed values to NULL rather than 0
+///
+//# This makes an effort to choose between !x and x == NULL.  !x is used
+//# if it has previously been used with the function used to initialize x.
+//# This relies on type information.  More type information can be obtained
+//# using the option -all_includes and the option -I to specify an
+//# include path.
+//
+// Confidence: High
+// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6.  GPLv2.
+// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6.  GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments:
+// Options:
+
+virtual patch
+virtual context
+virtual org
+virtual report
+
+@initialize:ocaml@
+let negtable = Hashtbl.create 101
+
+@depends on patch@
+expression *E;
+identifier f;
+@@
+
+(
+  (E = f(...)) ==
+- 0
++ NULL
+|
+  (E = f(...)) !=
+- 0
++ NULL
+|
+- 0
++ NULL
+  == (E = f(...))
+|
+- 0
++ NULL
+  != (E = f(...))
+)
+
+
+@t1 depends on !patch@
+expression *E;
+identifier f;
+position p;
+@@
+
+(
+  (E = f(...)) ==
+* 0@p
+|
+  (E = f(...)) !=
+* 0@p
+|
+* 0@p
+  == (E = f(...))
+|
+* 0@p
+  != (E = f(...))
+)
+
+@script:python depends on org@
+p << t1.p;
+@@
+
+coccilib.org.print_todo(p[0], "WARNING comparing pointer to 0")
+
+@script:python depends on report@
+p << t1.p;
+@@
+
+coccilib.report.print_report(p[0], "WARNING comparing pointer to 0")
+
+// Tests of returned values
+
+@s@
+identifier f;
+expression E,E1;
+@@
+
+ E = f(...)
+ ... when != E = E1
+ !E
+
+@script:ocaml depends on s@
+f << s.f;
+@@
+
+try let _ = Hashtbl.find negtable f in ()
+with Not_found -> Hashtbl.add negtable f ()
+
+@ r disable is_zero,isnt_zero exists @
+expression *E;
+identifier f;
+@@
+
+E = f(...)
+...
+(E == 0
+|E != 0
+|0 == E
+|0 != E
+)
+
+@script:ocaml@
+f << r.f;
+@@
+
+try let _ = Hashtbl.find negtable f in ()
+with Not_found -> include_match false
+
+// This rule may lead to inconsistent path problems, if E is defined in two
+// places
+@ depends on patch disable is_zero,isnt_zero @
+expression *E;
+expression E1;
+identifier r.f;
+@@
+
+E = f(...)
+<...
+(
+- E == 0
++ !E
+|
+- E != 0
++ E
+|
+- 0 == E
++ !E
+|
+- 0 != E
++ E
+)
+...>
+?E = E1
+
+@t2 depends on !patch disable is_zero,isnt_zero @
+expression *E;
+expression E1;
+identifier r.f;
+position p1;
+position p2;
+@@
+
+E = f(...)
+<...
+(
+* E == 0@p1
+|
+* E != 0@p2
+|
+* 0@p1 == E
+|
+* 0@p1 != E
+)
+...>
+?E = E1
+
+@script:python depends on org@
+p << t2.p1;
+@@
+
+coccilib.org.print_todo(p[0], "WARNING comparing pointer to 0, suggest !E")
+
+@script:python depends on org@
+p << t2.p2;
+@@
+
+coccilib.org.print_todo(p[0], "WARNING comparing pointer to 0")
+
+@script:python depends on report@
+p << t2.p1;
+@@
+
+coccilib.report.print_report(p[0], "WARNING comparing pointer to 0, suggest !E")
+
+@script:python depends on report@
+p << t2.p2;
+@@
+
+coccilib.report.print_report(p[0], "WARNING comparing pointer to 0")
+
+@ depends on patch disable is_zero,isnt_zero @
+expression *E;
+@@
+
+(
+  E ==
+- 0
++ NULL
+|
+  E !=
+- 0
++ NULL
+|
+- 0
++ NULL
+  == E
+|
+- 0
++ NULL
+  != E
+)
+
+@ t3 depends on !patch disable is_zero,isnt_zero @
+expression *E;
+position p;
+@@
+
+(
+* E == 0@p
+|
+* E != 0@p
+|
+* 0@p == E
+|
+* 0@p != E
+)
+
+@script:python depends on org@
+p << t3.p;
+@@
+
+coccilib.org.print_todo(p[0], "WARNING comparing pointer to 0")
+
+@script:python depends on report@
+p << t3.p;
+@@
+
+coccilib.report.print_report(p[0], "WARNING comparing pointer to 0")
index 451c92d31b196634cfb9ed7b2b15c632efd89691..2ef5e2e3dd38f781ef92fdd1d702e30c96fd7b20 100644 (file)
@@ -101,7 +101,7 @@ int main(int argc, char *argv[])
        const char *outform = "dts";
        const char *outname = "-";
        const char *depname = NULL;
-       int force = 0, check = 0, sort = 0;
+       int force = 0, sort = 0;
        const char *arg;
        int opt;
        FILE *outf = NULL;
@@ -143,9 +143,6 @@ int main(int argc, char *argv[])
                case 'f':
                        force = 1;
                        break;
-               case 'c':
-                       check = 1;
-                       break;
                case 'q':
                        quiet++;
                        break;
index ead0332c87e1b1e613507c79bbcda2aa87998309..28d0b2381df6e526a09043da4b79247ce1998c41 100644 (file)
@@ -697,7 +697,6 @@ static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb)
 {
        struct reserve_info *reservelist = NULL;
        struct reserve_info *new;
-       const char *p;
        struct fdt_reserve_entry re;
 
        /*
@@ -706,7 +705,6 @@ static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb)
         *
         * First pass, count entries.
         */
-       p = inb->ptr;
        while (1) {
                flat_read_chunk(inb, &re, sizeof(re));
                re.address  = fdt64_to_cpu(re.address);
index 7957e7a5166a26b4ab29988273f69006354e03eb..64ac2380e4d52c91de87df2d2f222c978ad39bee 100644 (file)
@@ -19,6 +19,7 @@
 # 3) Check for leaked CONFIG_ symbols
 
 use strict;
+use File::Basename;
 
 my ($dir, $arch, @files) = @ARGV;
 
@@ -99,6 +100,39 @@ sub check_asm_types
 }
 
 my $linux_types;
+my %import_stack = ();
+sub check_include_typesh
+{
+       my $path = $_[0];
+       my $import_path;
+
+       my $fh;
+       my @file_paths = ($path, $dir . "/" .  $path, dirname($filename) . "/" . $path);
+       for my $possible ( @file_paths ) {
+           if (not $import_stack{$possible} and open($fh, '<', $possible)) {
+               $import_path = $possible;
+               $import_stack{$import_path} = 1;
+               last;
+           }
+       }
+       if (eof $fh) {
+           return;
+       }
+
+       my $line;
+       while ($line = <$fh>) {
+               if ($line =~ m/^\s*#\s*include\s+<linux\/types.h>/) {
+                       $linux_types = 1;
+                       last;
+               }
+               if (my $included = ($line =~ /^\s*#\s*include\s+[<"](\S+)[>"]/)[0]) {
+                       check_include_typesh($included);
+               }
+       }
+       close $fh;
+       delete $import_stack{$import_path};
+}
+
 sub check_sizetypes
 {
        if ($filename =~ /types.h|int-l64.h|int-ll64.h/o) {
@@ -113,6 +147,9 @@ sub check_sizetypes
                $linux_types = 1;
                return;
        }
+       if (my $included = ($line =~ /^\s*#\s*include\s+[<"](\S+)[>"]/)[0]) {
+               check_include_typesh($included);
+       }
        if ($line =~ m/__[us](8|16|32|64)\b/) {
                printf STDERR "$filename:$lineno: " .
                              "found __[us]{8,16,32,64} type " .
@@ -122,4 +159,3 @@ sub check_sizetypes
                #$ret = 1;
        }
 }
-
index 7c7a5a6cc3f504bd759dc01019536b2bf804fbbd..0586085136d1304eebaa43142be468e4a7f40c8c 100644 (file)
@@ -344,10 +344,8 @@ setsym:
 
 int conf_read(const char *name)
 {
-       struct symbol *sym, *choice_sym;
-       struct property *prop;
-       struct expr *e;
-       int i, flags;
+       struct symbol *sym;
+       int i;
 
        sym_set_change_count(0);
 
@@ -357,7 +355,7 @@ int conf_read(const char *name)
        for_all_symbols(i, sym) {
                sym_calc_value(sym);
                if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO))
-                       goto sym_ok;
+                       continue;
                if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
                        /* check that calculated value agrees with saved value */
                        switch (sym->type) {
@@ -366,30 +364,18 @@ int conf_read(const char *name)
                                if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym))
                                        break;
                                if (!sym_is_choice(sym))
-                                       goto sym_ok;
+                                       continue;
                                /* fall through */
                        default:
                                if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val))
-                                       goto sym_ok;
+                                       continue;
                                break;
                        }
                } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE))
                        /* no previous value and not saved */
-                       goto sym_ok;
+                       continue;
                conf_unsaved++;
                /* maybe print value in verbose mode... */
-       sym_ok:
-               if (!sym_is_choice(sym))
-                       continue;
-               /* The choice symbol only has a set value (and thus is not new)
-                * if all its visible childs have values.
-                */
-               prop = sym_get_choice_prop(sym);
-               flags = sym->flags;
-               expr_list_for_each_sym(prop->expr, e, choice_sym)
-                       if (choice_sym->visible != no)
-                               flags &= choice_sym->flags;
-               sym->flags &= flags | ~SYMBOL_DEF_USER;
        }
 
        for_all_symbols(i, sym) {
old mode 100644 (file)
new mode 100755 (executable)
index ceadf0e..974d5cb
@@ -31,10 +31,12 @@ usage() {
        echo "  -h    display this help text"
        echo "  -m    only merge the fragments, do not execute the make command"
        echo "  -n    use allnoconfig instead of alldefconfig"
+       echo "  -r    list redundant entries when merging fragments"
 }
 
 MAKE=true
 ALLTARGET=alldefconfig
+WARNREDUN=false
 
 while true; do
        case $1 in
@@ -52,18 +54,27 @@ while true; do
                usage
                exit
                ;;
+       "-r")
+               WARNREDUN=true
+               shift
+               continue
+               ;;
        *)
                break
                ;;
        esac
 done
 
-
+INITFILE=$1
+shift;
 
 MERGE_LIST=$*
 SED_CONFIG_EXP="s/^\(# \)\{0,1\}\(CONFIG_[a-zA-Z0-9_]*\)[= ].*/\2/p"
 TMP_FILE=$(mktemp ./.tmp.config.XXXXXXXXXX)
 
+echo "Using $INITFILE as base"
+cat $INITFILE > $TMP_FILE
+
 # Merge files, printing warnings on overrided values
 for MERGE_FILE in $MERGE_LIST ; do
        echo "Merging $MERGE_FILE"
@@ -79,6 +90,8 @@ for MERGE_FILE in $MERGE_LIST ; do
                        echo Previous  value: $PREV_VAL
                        echo New value:       $NEW_VAL
                        echo
+                       elif [ "$WARNREDUN" = "true" ]; then
+                       echo Value of $CFG is redundant by fragment $MERGE_FILE:
                        fi
                        sed -i "/$CFG[ =]/d" $TMP_FILE
                fi
index 071f00c3046e69e77112a4e5cf56d4f686fc101f..22a3c400fc41119c8c8f4197c3dfcdde87b0a882 100644 (file)
@@ -262,11 +262,18 @@ static struct symbol *sym_calc_choice(struct symbol *sym)
        struct symbol *def_sym;
        struct property *prop;
        struct expr *e;
+       int flags;
 
        /* first calculate all choice values' visibilities */
+       flags = sym->flags;
        prop = sym_get_choice_prop(sym);
-       expr_list_for_each_sym(prop->expr, e, def_sym)
+       expr_list_for_each_sym(prop->expr, e, def_sym) {
                sym_calc_visibility(def_sym);
+               if (def_sym->visible != no)
+                       flags &= def_sym->flags;
+       }
+
+       sym->flags &= flags | ~SYMBOL_DEF_USER;
 
        /* is the user choice visible? */
        def_sym = sym->def[S_DEF_USER].val;
index 9adb667dd31aec6911a7049a6d3a4d91412b1d43..3f01fd9087308bb84564358101c5761b38368e1c 100644 (file)
@@ -849,7 +849,7 @@ static void check_section(const char *modname, struct elf_info *elf,
 
 #define ALL_INIT_DATA_SECTIONS \
        ".init.setup$", ".init.rodata$", \
-       ".devinit.rodata$", ".cpuinit.rodata$", ".meminit.rodata$" \
+       ".devinit.rodata$", ".cpuinit.rodata$", ".meminit.rodata$", \
        ".init.data$", ".devinit.data$", ".cpuinit.data$", ".meminit.data$"
 #define ALL_EXIT_DATA_SECTIONS \
        ".exit.data$", ".devexit.data$", ".cpuexit.data$", ".memexit.data$"
index 3c6c0b14c8073af178f83e238683b23f5e95e12b..eee5f8ed2493c7c2278e6f2ce6a9cb1993e17123 100644 (file)
@@ -97,6 +97,7 @@ mkdir -m 755 -p "$libc_headers_dir/DEBIAN"
 mkdir -p "$libc_headers_dir/usr/share/doc/$libc_headers_packagename"
 mkdir -m 755 -p "$kernel_headers_dir/DEBIAN"
 mkdir -p "$kernel_headers_dir/usr/share/doc/$kernel_headers_packagename"
+mkdir -p "$kernel_headers_dir/lib/modules/$version/"
 if [ "$ARCH" = "um" ] ; then
        mkdir -p "$tmpdir/usr/lib/uml/modules/$version" "$tmpdir/usr/bin"
 fi
@@ -120,15 +121,19 @@ else
 fi
 
 if grep -q '^CONFIG_MODULES=y' .config ; then
-       INSTALL_MOD_PATH="$tmpdir" make KBUILD_SRC= modules_install
+       INSTALL_MOD_PATH="$tmpdir" $MAKE KBUILD_SRC= modules_install
+       rm -f "$tmpdir/lib/modules/$version/build"
+       rm -f "$tmpdir/lib/modules/$version/source"
        if [ "$ARCH" = "um" ] ; then
                mv "$tmpdir/lib/modules/$version"/* "$tmpdir/usr/lib/uml/modules/$version/"
                rmdir "$tmpdir/lib/modules/$version"
        fi
 fi
 
-make headers_check
-make headers_install INSTALL_HDR_PATH="$libc_headers_dir/usr"
+if [ "$ARCH" != "um" ]; then
+       $MAKE headers_check KBUILD_SRC=
+       $MAKE headers_install KBUILD_SRC= INSTALL_HDR_PATH="$libc_headers_dir/usr"
+fi
 
 # Install the maintainer scripts
 # Note: hook scripts under /etc/kernel are also executed by official Debian
@@ -245,6 +250,7 @@ destdir=$kernel_headers_dir/usr/src/linux-headers-$version
 mkdir -p "$destdir"
 (cd $srctree; tar -c -f - -T "$objtree/debian/hdrsrcfiles") | (cd $destdir; tar -xf -)
 (cd $objtree; tar -c -f - -T "$objtree/debian/hdrobjfiles") | (cd $destdir; tar -xf -)
+ln -sf "/usr/src/linux-headers-$version" "$kernel_headers_dir/lib/modules/$version/build"
 rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles"
 arch=$(dpkg --print-architecture)
 
@@ -259,8 +265,6 @@ Description: Linux kernel headers for $KERNELRELEASE on $arch
  This is useful for people who need to build external modules
 EOF
 
-create_package "$kernel_headers_packagename" "$kernel_headers_dir"
-
 # Do we have firmware? Move it out of the way and build it into a package.
 if [ -e "$tmpdir/lib/firmware" ]; then
        mv "$tmpdir/lib/firmware" "$fwdir/lib/"
@@ -287,7 +291,11 @@ Description: Linux support headers for userspace development
  are used by the installed headers for GNU glibc and other system libraries.
 EOF
 
-create_package "$libc_headers_packagename" "$libc_headers_dir"
+if [ "$ARCH" != "um" ]; then
+       create_package "$kernel_headers_packagename" "$kernel_headers_dir"
+       create_package "$libc_headers_packagename" "$libc_headers_dir"
+fi
+
 create_package "$packagename" "$tmpdir"
 
 exit 0
index 20fb25c2338299f33a8954d6b046ce17f7b466fe..d000ea3a41fdb7f202a34e439cf954bd5353b74f 100755 (executable)
@@ -116,6 +116,10 @@ findFile () {
                ext=".bz2"
                name="bzip2"
                uncomp="bunzip2 -dc"
+  elif [ -r ${filebase}.xz ]; then
+                ext=".xz"
+                name="xz"
+                uncomp="xz -dc"
   elif [ -r ${filebase}.zip ]; then
                ext=".zip"
                name="zip"
index 4d403844e137a59f53f032bc4972a35e86b2327f..bd6dca8a0ab23b78e0499a8868ec08bdb92e48f3 100755 (executable)
@@ -75,8 +75,7 @@ scm_version()
                [ -w . ] && git update-index --refresh --unmerged > /dev/null
 
                # Check for uncommitted changes
-               if git diff-index --name-only HEAD | grep -v "^scripts/package" \
-                   | read dummy; then
+               if git diff-index --name-only HEAD | grep -qv "^scripts/package"; then
                        printf '%s' -dirty
                fi
 
index 833813a99e7c23a29c4e7147c58ac0ca2c696400..0d6004e20658e27b519874ac01c2499572906175 100755 (executable)
@@ -116,7 +116,7 @@ docscope()
 
 dogtags()
 {
-       all_sources | gtags -f -
+       all_sources | gtags -i -f -
 }
 
 exuberant()
@@ -166,9 +166,6 @@ exuberant()
        all_defconfigs | xargs -r $1 -a                         \
        --langdef=dotconfig --language-force=dotconfig          \
        --regex-dotconfig='/^#?[[:blank:]]*(CONFIG_[[:alnum:]_]+)/\1/'
-
-       # Remove structure forward declarations.
-       LANG=C sed -i -e '/^\([a-zA-Z_][a-zA-Z0-9_]*\)\t.*\t\/\^struct \1;.*\$\/;"\tx$/d' tags
 }
 
 emacs()
@@ -233,6 +230,7 @@ if [ "${ARCH}" = "um" ]; then
        fi
 fi
 
+remove_structs=
 case "$1" in
        "cscope")
                docscope
@@ -245,10 +243,17 @@ case "$1" in
        "tags")
                rm -f tags
                xtags ctags
+               remove_structs=y
                ;;
 
        "TAGS")
                rm -f TAGS
                xtags etags
+               remove_structs=y
                ;;
 esac
+
+# Remove structure forward declarations.
+if [ -n $remove_structs ]; then
+    LANG=C sed -i -e '/^\([a-zA-Z_][a-zA-Z0-9_]*\)\t.*\t\/\^struct \1;.*\$\/;"\tx$/d' $1
+fi
index 6989472d0957d89ab80e773c4ce1e2751de56cbe..1a70fa26da72683c4bd3fe343baa83117f5ed285 100644 (file)
@@ -513,7 +513,7 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
  * be performed under a lock, to allow the lock to be released
  * before calling the auditing code.
  */
-int avc_audit(u32 ssid, u32 tsid,
+inline int avc_audit(u32 ssid, u32 tsid,
               u16 tclass, u32 requested,
               struct av_decision *avd, int result, struct common_audit_data *a,
               unsigned flags)
@@ -741,6 +741,41 @@ int avc_ss_reset(u32 seqno)
        return rc;
 }
 
+/*
+ * Slow-path helper function for avc_has_perm_noaudit,
+ * when the avc_node lookup fails. We get called with
+ * the RCU read lock held, and need to return with it
+ * still held, but drop if for the security compute.
+ *
+ * Don't inline this, since it's the slow-path and just
+ * results in a bigger stack frame.
+ */
+static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid,
+                        u16 tclass, struct av_decision *avd)
+{
+       rcu_read_unlock();
+       security_compute_av(ssid, tsid, tclass, avd);
+       rcu_read_lock();
+       return avc_insert(ssid, tsid, tclass, avd);
+}
+
+static noinline int avc_denied(u32 ssid, u32 tsid,
+                        u16 tclass, u32 requested,
+                        unsigned flags,
+                        struct av_decision *avd)
+{
+       if (flags & AVC_STRICT)
+               return -EACCES;
+
+       if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE))
+               return -EACCES;
+
+       avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
+                               tsid, tclass, avd->seqno);
+       return 0;
+}
+
+
 /**
  * avc_has_perm_noaudit - Check permissions but perform no auditing.
  * @ssid: source security identifier
@@ -761,7 +796,7 @@ int avc_ss_reset(u32 seqno)
  * auditing, e.g. in cases where a lock must be held for the check but
  * should be released for the auditing.
  */
-int avc_has_perm_noaudit(u32 ssid, u32 tsid,
+inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
                         u16 tclass, u32 requested,
                         unsigned flags,
                         struct av_decision *avd)
@@ -776,26 +811,15 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
 
        node = avc_lookup(ssid, tsid, tclass);
        if (unlikely(!node)) {
-               rcu_read_unlock();
-               security_compute_av(ssid, tsid, tclass, avd);
-               rcu_read_lock();
-               node = avc_insert(ssid, tsid, tclass, avd);
+               node = avc_compute_av(ssid, tsid, tclass, avd);
        } else {
                memcpy(avd, &node->ae.avd, sizeof(*avd));
                avd = &node->ae.avd;
        }
 
        denied = requested & ~(avd->allowed);
-
-       if (denied) {
-               if (flags & AVC_STRICT)
-                       rc = -EACCES;
-               else if (!selinux_enforcing || (avd->flags & AVD_FLAGS_PERMISSIVE))
-                       avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
-                                       tsid, tclass, avd->seqno);
-               else
-                       rc = -EACCES;
-       }
+       if (unlikely(denied))
+               rc = avc_denied(ssid, tsid, tclass, requested, flags, avd);
 
        rcu_read_unlock();
        return rc;
index bbe32d2177d972f21bc25f8bc1784b6e886556c8..dbc55071679081568e6e3e89e256d78eccc74b55 100644 (file)
@@ -46,7 +46,7 @@
 
   The number of ports to be created can be specified via the module
   parameter "ports".  For example, to create four ports, add the
-  following option in /etc/modprobe.conf:
+  following option in a configuration file under /etc/modprobe.d/:
 
        option snd-seq-dummy ports=4
 
index c8961165277cce21a85147d6571cf014e86bf13d..fe5ae09ffccba585392e786f26942265e0d90650 100644 (file)
@@ -50,7 +50,8 @@ config SND_PCSP
          before the other sound driver of yours, making the
          pc-speaker a default sound device. Which is likely not
          what you want. To make this driver play nicely with other
-         sound driver, you can add this into your /etc/modprobe.conf:
+         sound driver, you can add this in a configuration file under
+         /etc/modprobe.d/ directory:
          options snd-pcsp index=2
 
          You don't need this driver if you only want your pc-speaker to beep.
index babaedd242f70b0fafb71dbcbdd8071c19c3f892..d7ccf28bd66a8263e3e4cbac4adbb854b506b852 100644 (file)
@@ -65,7 +65,7 @@ static int index = SNDRV_DEFAULT_IDX1;        /* Index 0-MAX */
 static char *id = SNDRV_DEFAULT_STR1;          /* ID for this card */
 //static bool enable = SNDRV_DEFAULT_ENABLE1;  /* Enable this card */
 #ifdef CONFIG_PNP
-static int isapnp = 1;                 /* Enable ISA PnP detection */
+static bool isapnp = true;                     /* Enable ISA PnP detection */
 #endif
 static long port = SNDRV_DEFAULT_PORT1;        /* 0x530,0xe80,0xf40,0x604 */
 static long mpu_port = SNDRV_DEFAULT_PORT1;    /* 0x300,0x310,0x320,0x330 */
index eba734560f6f799896b4ab7b59edd0a57a44c901..2c79d60a725f45dd83f31edf6187204ed8c4db1d 100644 (file)
@@ -1631,7 +1631,7 @@ static int ide_irq __initdata = 0;
 static int joystick_io __initdata = 0;
 
 /* If we have the digital daugherboard... */
-static int digital __initdata = 0;
+static bool digital __initdata = false;
 #endif
 
 static int fifosize __initdata =       DEFFIFOSIZE;
index 4cc315daeda0dcfcbd04d976aa6b7b02f13a1656..8c63200cf339631d9c18649c4a9f5195f650e9c9 100644 (file)
@@ -42,7 +42,7 @@ On error *pLockedMemHandle marked invalid, non-zero returned.
 If this function succeeds, then HpiOs_LockedMem_GetVirtAddr() and
 HpiOs_LockedMem_GetPyhsAddr() will always succed on the returned handle.
 */
-u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_locked_mem_handle,
+int hpios_locked_mem_alloc(struct consistent_dma_area *p_locked_mem_handle,
                                                           /**< memory handle */
        u32 size, /**< Size in bytes to allocate */
        struct pci_dev *p_os_reference
index 2d7d1c2e1d0d2553e8fbe180b7a41a66a9c4b2af..87f4385fe8c7f1a6b976734f0b56b19706d4d67b 100644 (file)
@@ -43,7 +43,7 @@ void hpios_delay_micro_seconds(u32 num_micro_sec)
 
 On error, return -ENOMEM, and *pMemArea.size = 0
 */
-u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
+int hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
        struct pci_dev *pdev)
 {
        /*?? any benefit in using managed dmam_alloc_coherent? */
index 8ea2fd65432718411776151a9b034a1251c46a49..9917e55d6f11f8bbe9c0e8c7827febaf6c6ba6f7 100644 (file)
@@ -2717,9 +2717,6 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
        int max_nums = ARRAY_SIZE(spec->private_adc_nids);
        int i, nums = 0;
 
-       if (spec->shared_mic_hp)
-               max_nums = 1; /* no multi streams with the shared HP/mic */
-
        nid = codec->start_nid;
        for (i = 0; i < codec->num_nodes; i++, nid++) {
                hda_nid_t src;
@@ -4076,6 +4073,7 @@ static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
        if (spec->dyn_adc_switch)
                return;
 
+ again:
        nums = 0;
        for (n = 0; n < spec->num_adc_nids; n++) {
                hda_nid_t cap = spec->private_capsrc_nids[n];
@@ -4096,6 +4094,11 @@ static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
        if (!nums) {
                /* check whether ADC-switch is possible */
                if (!alc_check_dyn_adc_switch(codec)) {
+                       if (spec->shared_mic_hp) {
+                               spec->shared_mic_hp = 0;
+                               spec->private_imux[0].num_items = 1;
+                               goto again;
+                       }
                        printk(KERN_WARNING "hda_codec: %s: no valid ADC found;"
                               " using fallback 0x%x\n",
                               codec->chip_name, spec->private_adc_nids[0]);
@@ -4113,7 +4116,7 @@ static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
 
        if (spec->auto_mic)
                alc_auto_mic_check_imux(codec); /* check auto-mic setups */
-       else if (spec->input_mux->num_items == 1)
+       else if (spec->input_mux->num_items == 1 || spec->shared_mic_hp)
                spec->num_adc_nids = 1; /* reduce to a single ADC */
 }
 
index fe7fbaeb714658c716d0091894ee892d97e0b5d8..7c49642af05249197f233c94832a16e6792200f5 100644 (file)
@@ -3629,7 +3629,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                case 2:
                case 3:
                        wm8994->hubs.dcs_codes_l = -9;
-                       wm8994->hubs.dcs_codes_r = -5;
+                       wm8994->hubs.dcs_codes_r = -7;
                        break;
                default:
                        break;
index 6ca1f46d84a49cc5d0cd5ab2b886975ec63879b9..e373fbbc97a0eb7edeb06bb99dfee274a129249c 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/dmaengine.h>
+#include <linux/fsl/mxs-dma.h>
 
 #include <sound/core.h>
 #include <sound/initval.h>
@@ -36,7 +37,6 @@
 #include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
 
-#include <mach/dma.h>
 #include "mxs-pcm.h"
 
 struct mxs_pcm_dma_data {
index 12be05b16880aacc05d896d15564fd548686bb55..53f4fd8fecedc81d418e38730b7b3d1040967ad4 100644 (file)
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/time.h>
+#include <linux/fsl/mxs-dma.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/saif.h>
-#include <mach/dma.h>
 #include <asm/mach-types.h>
 #include <mach/hardware.h>
 #include <mach/mxs.h>
index 4420b7030c837a477eb8d143c0d8ec99b7d64913..475695234b3d2f37d843e34e2c3e8caa4df0b4c7 100644 (file)
@@ -143,7 +143,7 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
        direction = snd_pcm_substream_to_dma_direction(substream);
 
        prtd->pos = 0;
-       desc = chan->device->device_prep_dma_cyclic(chan,
+       desc = dmaengine_prep_dma_cyclic(chan,
                substream->runtime->dma_addr,
                snd_pcm_lib_buffer_bytes(substream),
                snd_pcm_lib_period_bytes(substream), direction);
index 87feeee8b90c4137a0fe5d5260a31ca98a3c3766..2d89f02719b5f6ce52e502414c031a2d7610ca9a 100644 (file)
@@ -48,6 +48,9 @@ OPTIONS
        Only consider these symbols. CSV that understands
        file://filename entries.
 
+--symbol-filter=::
+       Only show symbols that match (partially) with this filter.
+
 -U::
 --hide-unresolved::
         Only display entries resolved to a symbol.
@@ -110,6 +113,8 @@ OPTIONS
        requires a tty, if one is not present, as when piping to other
        commands, the stdio interface is used.
 
+--gtk:: Use the GTK2 interface.
+
 -k::
 --vmlinux=<file>::
         vmlinux pathname
index 74fd7f89208a092e5312785317aa6ed49a81741e..820371f10d1b1b96e1be9332d454f6ee053aaf34 100644 (file)
@@ -182,7 +182,7 @@ endif
 
 ### --- END CONFIGURATION SECTION ---
 
-BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
+BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
 BASIC_LDFLAGS =
 
 # Guard against environment variables
@@ -234,6 +234,25 @@ endif
 
 export PERL_PATH
 
+FLEX = $(CROSS_COMPILE)flex
+BISON= $(CROSS_COMPILE)bison
+
+event-parser:
+       $(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o $(OUTPUT)util/parse-events-bison.c
+       $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c
+
+$(OUTPUT)util/parse-events-flex.c: event-parser
+$(OUTPUT)util/parse-events-bison.c: event-parser
+
+pmu-parser:
+       $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c
+       $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c
+
+$(OUTPUT)util/pmu-flex.c: pmu-parser
+$(OUTPUT)util/pmu-bison.c: pmu-parser
+
+$(OUTPUT)util/parse-events.o: event-parser pmu-parser
+
 LIB_FILE=$(OUTPUT)libperf.a
 
 LIB_H += ../../include/linux/perf_event.h
@@ -249,7 +268,7 @@ LIB_H += util/include/linux/const.h
 LIB_H += util/include/linux/ctype.h
 LIB_H += util/include/linux/kernel.h
 LIB_H += util/include/linux/list.h
-LIB_H += util/include/linux/module.h
+LIB_H += util/include/linux/export.h
 LIB_H += util/include/linux/poison.h
 LIB_H += util/include/linux/prefetch.h
 LIB_H += util/include/linux/rbtree.h
@@ -276,6 +295,7 @@ LIB_H += util/build-id.h
 LIB_H += util/debug.h
 LIB_H += util/debugfs.h
 LIB_H += util/sysfs.h
+LIB_H += util/pmu.h
 LIB_H += util/event.h
 LIB_H += util/evsel.h
 LIB_H += util/evlist.h
@@ -323,6 +343,7 @@ LIB_OBJS += $(OUTPUT)util/config.o
 LIB_OBJS += $(OUTPUT)util/ctype.o
 LIB_OBJS += $(OUTPUT)util/debugfs.o
 LIB_OBJS += $(OUTPUT)util/sysfs.o
+LIB_OBJS += $(OUTPUT)util/pmu.o
 LIB_OBJS += $(OUTPUT)util/environment.o
 LIB_OBJS += $(OUTPUT)util/event.o
 LIB_OBJS += $(OUTPUT)util/evlist.o
@@ -359,6 +380,10 @@ LIB_OBJS += $(OUTPUT)util/session.o
 LIB_OBJS += $(OUTPUT)util/thread.o
 LIB_OBJS += $(OUTPUT)util/thread_map.o
 LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
+LIB_OBJS += $(OUTPUT)util/parse-events-flex.o
+LIB_OBJS += $(OUTPUT)util/parse-events-bison.o
+LIB_OBJS += $(OUTPUT)util/pmu-flex.o
+LIB_OBJS += $(OUTPUT)util/pmu-bison.o
 LIB_OBJS += $(OUTPUT)util/trace-event-read.o
 LIB_OBJS += $(OUTPUT)util/trace-event-info.o
 LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
@@ -501,6 +526,20 @@ else
        endif
 endif
 
+ifdef NO_GTK2
+       BASIC_CFLAGS += -DNO_GTK2
+else
+       FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0)
+       ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y)
+               msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
+               BASIC_CFLAGS += -DNO_GTK2_SUPPORT
+       else
+               BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
+               EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
+               LIB_OBJS += $(OUTPUT)util/gtk/browser.o
+       endif
+endif
+
 ifdef NO_LIBPERL
        BASIC_CFLAGS += -DNO_LIBPERL
 else
@@ -647,6 +686,8 @@ ifndef V
        QUIET_LINK     = @echo '   ' LINK $@;
        QUIET_MKDIR    = @echo '   ' MKDIR $@;
        QUIET_GEN      = @echo '   ' GEN $@;
+       QUIET_FLEX     = @echo '   ' FLEX $@;
+       QUIET_BISON    = @echo '   ' BISON $@;
 endif
 endif
 
@@ -727,12 +768,28 @@ $(OUTPUT)perf.o perf.spec \
        $(SCRIPTS) \
        : $(OUTPUT)PERF-VERSION-FILE
 
+.SUFFIXES:
+.SUFFIXES: .o .c .S .s
+
+# These two need to be here so that when O= is not used they take precedence
+# over the general rule for .o
+
+$(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
+
+$(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
+
 $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+$(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $<
 $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS
-       $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
+       $(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $<
 $(OUTPUT)%.o: %.S
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
+$(OUTPUT)%.s: %.S
+       $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $<
 
 $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
@@ -795,6 +852,8 @@ help:
        @echo '  html           - make html documentation'
        @echo '  info           - make GNU info documentation (access with info <foo>)'
        @echo '  pdf            - make pdf documentation'
+       @echo '  event-parser   - make event parser code'
+       @echo '  pmu-parser     - make pmu format parser code'
        @echo '  TAGS           - use etags to make tag information for source browsing'
        @echo '  tags           - use ctags to make tag information for source browsing'
        @echo '  cscope - use cscope to make interactive browsing database'
@@ -931,6 +990,7 @@ clean:
        $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
        $(MAKE) -C Documentation/ clean
        $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS
+       $(RM) $(OUTPUT)util/*-{bison,flex}*
        $(python-clean)
 
 .PHONY: all install clean strip
index 4f19513d7dda068eed48141ee4b3b300381cdc3d..d29d350fb2b731187d92233d6c0ddb5ef2a368a6 100644 (file)
@@ -24,6 +24,11 @@ static char    diff__default_sort_order[] = "dso,symbol";
 static bool  force;
 static bool show_displacement;
 
+struct perf_diff {
+       struct perf_tool tool;
+       struct perf_session *session;
+};
+
 static int hists__add_entry(struct hists *self,
                            struct addr_location *al, u64 period)
 {
@@ -32,12 +37,14 @@ static int hists__add_entry(struct hists *self,
        return -ENOMEM;
 }
 
-static int diff__process_sample_event(struct perf_tool *tool __used,
+static int diff__process_sample_event(struct perf_tool *tool,
                                      union perf_event *event,
                                      struct perf_sample *sample,
                                      struct perf_evsel *evsel __used,
                                      struct machine *machine)
 {
+       struct perf_diff *_diff = container_of(tool, struct perf_diff, tool);
+       struct perf_session *session = _diff->session;
        struct addr_location al;
 
        if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
@@ -49,24 +56,26 @@ static int diff__process_sample_event(struct perf_tool *tool __used,
        if (al.filtered || al.sym == NULL)
                return 0;
 
-       if (hists__add_entry(&evsel->hists, &al, sample->period)) {
+       if (hists__add_entry(&session->hists, &al, sample->period)) {
                pr_warning("problem incrementing symbol period, skipping event\n");
                return -1;
        }
 
-       evsel->hists.stats.total_period += sample->period;
+       session->hists.stats.total_period += sample->period;
        return 0;
 }
 
-static struct perf_tool perf_diff = {
-       .sample = diff__process_sample_event,
-       .mmap   = perf_event__process_mmap,
-       .comm   = perf_event__process_comm,
-       .exit   = perf_event__process_task,
-       .fork   = perf_event__process_task,
-       .lost   = perf_event__process_lost,
-       .ordered_samples = true,
-       .ordering_requires_timestamps = true,
+static struct perf_diff diff = {
+       .tool = {
+               .sample = diff__process_sample_event,
+               .mmap   = perf_event__process_mmap,
+               .comm   = perf_event__process_comm,
+               .exit   = perf_event__process_task,
+               .fork   = perf_event__process_task,
+               .lost   = perf_event__process_lost,
+               .ordered_samples = true,
+               .ordering_requires_timestamps = true,
+       },
 };
 
 static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
@@ -107,12 +116,6 @@ static void hists__resort_entries(struct hists *self)
        self->entries = tmp;
 }
 
-static void hists__set_positions(struct hists *self)
-{
-       hists__output_resort(self);
-       hists__resort_entries(self);
-}
-
 static struct hist_entry *hists__find_entry(struct hists *self,
                                            struct hist_entry *he)
 {
@@ -146,30 +149,37 @@ static void hists__match(struct hists *older, struct hists *newer)
 static int __cmd_diff(void)
 {
        int ret, i;
+#define older (session[0])
+#define newer (session[1])
        struct perf_session *session[2];
 
-       session[0] = perf_session__new(input_old, O_RDONLY, force, false, &perf_diff);
-       session[1] = perf_session__new(input_new, O_RDONLY, force, false, &perf_diff);
+       older = perf_session__new(input_old, O_RDONLY, force, false,
+                                 &diff.tool);
+       newer = perf_session__new(input_new, O_RDONLY, force, false,
+                                 &diff.tool);
        if (session[0] == NULL || session[1] == NULL)
                return -ENOMEM;
 
        for (i = 0; i < 2; ++i) {
-               ret = perf_session__process_events(session[i], &perf_diff);
+               diff.session = session[i];
+               ret = perf_session__process_events(session[i], &diff.tool);
                if (ret)
                        goto out_delete;
+               hists__output_resort(&session[i]->hists);
        }
 
-       hists__output_resort(&session[1]->hists);
        if (show_displacement)
-               hists__set_positions(&session[0]->hists);
+               hists__resort_entries(&older->hists);
 
-       hists__match(&session[0]->hists, &session[1]->hists);
-       hists__fprintf(&session[1]->hists, &session[0]->hists,
+       hists__match(&older->hists, &newer->hists);
+       hists__fprintf(&newer->hists, &older->hists,
                       show_displacement, true, 0, 0, stdout);
 out_delete:
        for (i = 0; i < 2; ++i)
                perf_session__delete(session[i]);
        return ret;
+#undef older
+#undef newer
 }
 
 static const char * const diff_usage[] = {
index 8e91c6eba18adbcc7b1c8648d017699806b9d289..2e317438980b4767bbca4fdc3512cbd55d4ad1d2 100644 (file)
@@ -40,7 +40,7 @@ struct perf_report {
        struct perf_tool        tool;
        struct perf_session     *session;
        char const              *input_name;
-       bool                    force, use_tui, use_stdio;
+       bool                    force, use_tui, use_gtk, use_stdio;
        bool                    hide_unresolved;
        bool                    dont_use_callchains;
        bool                    show_full_info;
@@ -50,6 +50,7 @@ struct perf_report {
        const char              *pretty_printing_style;
        symbol_filter_t         annotate_init;
        const char              *cpu_list;
+       const char              *symbol_filter_str;
        DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
 };
 
@@ -400,6 +401,9 @@ static int __cmd_report(struct perf_report *rep)
        list_for_each_entry(pos, &session->evlist->entries, node) {
                struct hists *hists = &pos->hists;
 
+               if (pos->idx == 0)
+                       hists->symbol_filter_str = rep->symbol_filter_str;
+
                hists__collapse_resort(hists);
                hists__output_resort(hists);
                nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
@@ -411,8 +415,13 @@ static int __cmd_report(struct perf_report *rep)
        }
 
        if (use_browser > 0) {
-               perf_evlist__tui_browse_hists(session->evlist, help,
-                                             NULL, NULL, 0);
+               if (use_browser == 1) {
+                       perf_evlist__tui_browse_hists(session->evlist, help,
+                                                     NULL, NULL, 0);
+               } else if (use_browser == 2) {
+                       perf_evlist__gtk_browse_hists(session->evlist, help,
+                                                     NULL, NULL, 0);
+               }
        } else
                perf_evlist__tty_browse_hists(session->evlist, rep, help);
 
@@ -569,6 +578,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
        OPT_STRING(0, "pretty", &report.pretty_printing_style, "key",
                   "pretty printing style key: normal raw"),
        OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"),
+       OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"),
        OPT_BOOLEAN(0, "stdio", &report.use_stdio,
                    "Use the stdio interface"),
        OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
@@ -591,6 +601,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
                   "only consider symbols in these comms"),
        OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
                   "only consider these symbols"),
+       OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter",
+                  "only show symbols that (partially) match with this filter"),
        OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
                   "width[,width...]",
                   "don't try to adjust column width, use these fixed values"),
@@ -624,6 +636,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
                use_browser = 0;
        else if (report.use_tui)
                use_browser = 1;
+       else if (report.use_gtk)
+               use_browser = 2;
 
        if (report.inverted_callchain)
                callchain_param.order = ORDER_CALLER;
@@ -660,7 +674,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
        }
 
        if (strcmp(report.input_name, "-") != 0) {
-               setup_browser(true);
+               if (report.use_gtk)
+                       perf_gtk_setup_browser(argc, argv, true);
+               else
+                       setup_browser(true);
        } else {
                use_browser = 0;
        }
@@ -709,11 +726,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
        } else
                symbol_conf.exclude_other = false;
 
-       /*
-        * Any (unrecognized) arguments left?
-        */
-       if (argc)
-               usage_with_options(report_usage, options);
+       if (argc) {
+               /*
+                * Special case: if there's an argument left then assume that
+                * it's a symbol filter:
+                */
+               if (argc > 1)
+                       usage_with_options(report_usage, options);
+
+               report.symbol_filter_str = argv[0];
+       }
 
        sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
 
index ea40e4e8b2271f0a7a53ee25d8154cdcee4b19bd..c941bb640f4990f8590f8fb410dfaa0247bcc3b8 100644 (file)
@@ -296,7 +296,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel,
        if (system_wide)
                return perf_evsel__open_per_cpu(evsel, evsel_list->cpus,
                                                group, group_fd);
-       if (!target_pid && !target_tid) {
+       if (!target_pid && !target_tid && (!group || evsel == first)) {
                attr->disabled = 1;
                attr->enable_on_exec = 1;
        }
index 3e087ce8daa63fce8572f50ab60f9c1aab0e4531..1c5b9801ac6115547039599ff1424048efefd0e7 100644 (file)
@@ -13,6 +13,7 @@
 #include "util/parse-events.h"
 #include "util/symbol.h"
 #include "util/thread_map.h"
+#include "util/pmu.h"
 #include "../../include/linux/hw_breakpoint.h"
 
 #include <sys/mman.h>
@@ -650,7 +651,7 @@ static int test__checkevent_raw(struct perf_evlist *evlist)
 
        TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
-       TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config);
        return 0;
 }
 
@@ -677,6 +678,24 @@ static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
        return 0;
 }
 
+static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel = list_entry(evlist->entries.next,
+                                             struct perf_evsel, node);
+
+       TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong period",
+                       100000 == evsel->attr.sample_period);
+       TEST_ASSERT_VAL("wrong config1",
+                       0 == evsel->attr.config1);
+       TEST_ASSERT_VAL("wrong config2",
+                       1 == evsel->attr.config2);
+       return 0;
+}
+
 static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
 {
        struct perf_evsel *evsel = list_entry(evlist->entries.next,
@@ -858,6 +877,115 @@ static int test__checkevent_genhw_modifier(struct perf_evlist *evlist)
        return test__checkevent_genhw(evlist);
 }
 
+static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel = list_entry(evlist->entries.next,
+                                             struct perf_evsel, node);
+
+       TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+       return test__checkevent_breakpoint(evlist);
+}
+
+static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel = list_entry(evlist->entries.next,
+                                             struct perf_evsel, node);
+
+       TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+       return test__checkevent_breakpoint_x(evlist);
+}
+
+static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel = list_entry(evlist->entries.next,
+                                             struct perf_evsel, node);
+
+       TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+       return test__checkevent_breakpoint_r(evlist);
+}
+
+static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel = list_entry(evlist->entries.next,
+                                             struct perf_evsel, node);
+
+       TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+       return test__checkevent_breakpoint_w(evlist);
+}
+
+static int test__checkevent_pmu(struct perf_evlist *evlist)
+{
+
+       struct perf_evsel *evsel = list_entry(evlist->entries.next,
+                                             struct perf_evsel, node);
+
+       TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",    10 == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong config1",    1 == evsel->attr.config1);
+       TEST_ASSERT_VAL("wrong config2",    3 == evsel->attr.config2);
+       TEST_ASSERT_VAL("wrong period",  1000 == evsel->attr.sample_period);
+
+       return 0;
+}
+
+static int test__checkevent_list(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel;
+
+       TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+
+       /* r1 */
+       evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1);
+       TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2);
+       TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+       /* syscalls:sys_enter_open:k */
+       evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong sample_type",
+               (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
+               evsel->attr.sample_type);
+       TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
+       TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+       /* 1:1:hp */
+       evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+       TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+       return 0;
+}
+
 static struct test__event_st {
        const char *name;
        __u32 type;
@@ -872,7 +1000,7 @@ static struct test__event_st {
                .check = test__checkevent_tracepoint_multi,
        },
        {
-               .name  = "r1",
+               .name  = "r1a",
                .check = test__checkevent_raw,
        },
        {
@@ -883,6 +1011,10 @@ static struct test__event_st {
                .name  = "instructions",
                .check = test__checkevent_symbolic_name,
        },
+       {
+               .name  = "cycles/period=100000,config2/",
+               .check = test__checkevent_symbolic_name_config,
+       },
        {
                .name  = "faults",
                .check = test__checkevent_symbolic_alias,
@@ -916,7 +1048,7 @@ static struct test__event_st {
                .check = test__checkevent_tracepoint_multi_modifier,
        },
        {
-               .name  = "r1:kp",
+               .name  = "r1a:kp",
                .check = test__checkevent_raw_modifier,
        },
        {
@@ -935,6 +1067,30 @@ static struct test__event_st {
                .name  = "L1-dcache-load-miss:kp",
                .check = test__checkevent_genhw_modifier,
        },
+       {
+               .name  = "mem:0:u",
+               .check = test__checkevent_breakpoint_modifier,
+       },
+       {
+               .name  = "mem:0:x:k",
+               .check = test__checkevent_breakpoint_x_modifier,
+       },
+       {
+               .name  = "mem:0:r:hp",
+               .check = test__checkevent_breakpoint_r_modifier,
+       },
+       {
+               .name  = "mem:0:w:up",
+               .check = test__checkevent_breakpoint_w_modifier,
+       },
+       {
+               .name  = "cpu/config=10,config1,config2=3,period=1000/u",
+               .check = test__checkevent_pmu,
+       },
+       {
+               .name  = "r1,syscalls:sys_enter_open:k,1:1:hp",
+               .check = test__checkevent_list,
+       },
 };
 
 #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st))
@@ -960,10 +1116,9 @@ static int test__parse_events(void)
                }
 
                ret = e->check(evlist);
+               perf_evlist__delete(evlist);
                if (ret)
                        break;
-
-               perf_evlist__delete(evlist);
        }
 
        return ret;
@@ -1462,6 +1617,11 @@ static int test__rdpmc(void)
 
 #endif
 
+static int test__perf_pmu(void)
+{
+       return perf_pmu__test();
+}
+
 static struct test {
        const char *desc;
        int (*func)(void);
@@ -1496,6 +1656,10 @@ static struct test {
                .desc = "Validate PERF_RECORD_* events & perf_sample fields",
                .func = test__PERF_RECORD,
        },
+       {
+               .desc = "Test perf pmu format parsing",
+               .func = test__perf_pmu,
+       },
        {
                .func = NULL,
        },
index 6170fd2531b5e681ca1986fa0b231d3b3029382b..d9084e03ce56676628736c5814e27badbe2ef730 100644 (file)
@@ -65,6 +65,21 @@ int main(void)
 endef
 endif
 
+ifndef NO_GTK2
+define SOURCE_GTK2
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#include <gtk/gtk.h>
+#pragma GCC diagnostic error \"-Wstrict-prototypes\"
+
+int main(int argc, char *argv[])
+{
+        gtk_init(&argc, &argv);
+
+        return 0;
+}
+endef
+endif
+
 ifndef NO_LIBPERL
 define SOURCE_PERL_EMBED
 #include <EXTERN.h>
index e5a462f1d07c0c0ab6cbcc08bd1e4a317bac6693..199f69ec656f7a77c5c490e2f1195c9e09226e98 100644 (file)
@@ -28,8 +28,8 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym)
 int symbol__alloc_hist(struct symbol *sym)
 {
        struct annotation *notes = symbol__annotation(sym);
-       size_t sizeof_sym_hist = (sizeof(struct sym_hist) +
-                                 (sym->end - sym->start) * sizeof(u64));
+       const size_t size = sym->end - sym->start + 1;
+       size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64));
 
        notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);
        if (notes->src == NULL)
@@ -64,7 +64,7 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
 
        pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
 
-       if (addr >= sym->end)
+       if (addr > sym->end)
                return 0;
 
        offset = addr - sym->start;
@@ -408,7 +408,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
        if (!notes->src->lines)
                return -1;
 
-       start = map->unmap_ip(map, sym->start);
+       start = map__rip_2objdump(map, sym->start);
 
        for (i = 0; i < len; i++) {
                char *path = NULL;
index fc5e5a09d5b94102b7adaed11518dd8be2c0eb84..8dd224df3e54ce4f88c8f09b852c07e93656442c 100644 (file)
@@ -45,6 +45,18 @@ void setup_browser(bool fallback_to_pager);
 void exit_browser(bool wait_for_ok);
 #endif
 
+#ifdef NO_GTK2_SUPPORT
+static inline void perf_gtk_setup_browser(int argc __used, const char *argv[] __used, bool fallback_to_pager)
+{
+       if (fallback_to_pager)
+               setup_pager();
+}
+static inline void perf_gtk_exit_browser(bool wait_for_ok __used) {}
+#else
+void perf_gtk_setup_browser(int argc, const char *argv[], bool fallback_to_pager);
+void perf_gtk_exit_browser(bool wait_for_ok);
+#endif
+
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 
index 159263d17c2d31926672454004748b3583fe6f78..1986d8051bd16a9f133159545fb7083df3511d76 100644 (file)
@@ -51,13 +51,15 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
 void perf_evlist__config_attrs(struct perf_evlist *evlist,
                               struct perf_record_opts *opts)
 {
-       struct perf_evsel *evsel;
+       struct perf_evsel *evsel, *first;
 
        if (evlist->cpus->map[0] < 0)
                opts->no_inherit = true;
 
+       first = list_entry(evlist->entries.next, struct perf_evsel, node);
+
        list_for_each_entry(evsel, &evlist->entries, node) {
-               perf_evsel__config(evsel, opts);
+               perf_evsel__config(evsel, opts, first);
 
                if (evlist->nr_entries > 1)
                        evsel->attr.sample_type |= PERF_SAMPLE_ID;
index f421f7cbc0d34871a3ed7d58f064b1538547fc12..8c13dbcb84b93bee3527ebd3e53b48eaa23d5f11 100644 (file)
@@ -34,7 +34,7 @@ int __perf_evsel__sample_size(u64 sample_type)
        return size;
 }
 
-static void hists__init(struct hists *hists)
+void hists__init(struct hists *hists)
 {
        memset(hists, 0, sizeof(*hists));
        hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
@@ -63,7 +63,8 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
        return evsel;
 }
 
-void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
+void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
+                       struct perf_evsel *first)
 {
        struct perf_event_attr *attr = &evsel->attr;
        int track = !evsel->idx; /* only the first counter needs these */
@@ -134,7 +135,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
        attr->mmap = track;
        attr->comm = track;
 
-       if (!opts->target_pid && !opts->target_tid && !opts->system_wide) {
+       if (!opts->target_pid && !opts->target_tid && !opts->system_wide &&
+           (!opts->group || evsel == first)) {
                attr->disabled = 1;
                attr->enable_on_exec = 1;
        }
@@ -578,6 +580,8 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
                        return -EFAULT;
 
                data->raw_data = (void *) pdata;
+
+               array = (void *)array + data->raw_size + sizeof(u32);
        }
 
        if (type & PERF_SAMPLE_BRANCH_STACK) {
index 326b8e4d503579cb7306e8c05d82d95e957aa2d3..3d6b3e4cb66bb9bfbb32b75958ef1f23a4880163 100644 (file)
@@ -80,7 +80,8 @@ void perf_evsel__exit(struct perf_evsel *evsel);
 void perf_evsel__delete(struct perf_evsel *evsel);
 
 void perf_evsel__config(struct perf_evsel *evsel,
-                       struct perf_record_opts *opts);
+                       struct perf_record_opts *opts,
+                       struct perf_evsel *first);
 
 int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
 int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
@@ -169,4 +170,6 @@ static inline int perf_evsel__sample_size(struct perf_evsel *evsel)
        return __perf_evsel__sample_size(evsel->attr.sample_type);
 }
 
+void hists__init(struct hists *hists);
+
 #endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/gtk/browser.c b/tools/perf/util/gtk/browser.c
new file mode 100644 (file)
index 0000000..258352a
--- /dev/null
@@ -0,0 +1,189 @@
+#include "../evlist.h"
+#include "../cache.h"
+#include "../evsel.h"
+#include "../sort.h"
+#include "../hist.h"
+#include "gtk.h"
+
+#include <signal.h>
+
+#define MAX_COLUMNS                    32
+
+void perf_gtk_setup_browser(int argc, const char *argv[],
+                           bool fallback_to_pager __used)
+{
+       gtk_init(&argc, (char ***)&argv);
+}
+
+void perf_gtk_exit_browser(bool wait_for_ok __used)
+{
+       gtk_main_quit();
+}
+
+static void perf_gtk_signal(int sig)
+{
+       psignal(sig, "perf");
+       gtk_main_quit();
+}
+
+static void perf_gtk_resize_window(GtkWidget *window)
+{
+       GdkRectangle rect;
+       GdkScreen *screen;
+       int monitor;
+       int height;
+       int width;
+
+       screen = gtk_widget_get_screen(window);
+
+       monitor = gdk_screen_get_monitor_at_window(screen, window->window);
+
+       gdk_screen_get_monitor_geometry(screen, monitor, &rect);
+
+       width   = rect.width * 3 / 4;
+       height  = rect.height * 3 / 4;
+
+       gtk_window_resize(GTK_WINDOW(window), width, height);
+}
+
+static void perf_gtk_show_hists(GtkWidget *window, struct hists *hists)
+{
+       GType col_types[MAX_COLUMNS];
+       GtkCellRenderer *renderer;
+       struct sort_entry *se;
+       GtkListStore *store;
+       struct rb_node *nd;
+       u64 total_period;
+       GtkWidget *view;
+       int col_idx;
+       int nr_cols;
+
+       nr_cols = 0;
+
+       /* The percentage column */
+       col_types[nr_cols++] = G_TYPE_STRING;
+
+       list_for_each_entry(se, &hist_entry__sort_list, list) {
+               if (se->elide)
+                       continue;
+
+               col_types[nr_cols++] = G_TYPE_STRING;
+       }
+
+       store = gtk_list_store_newv(nr_cols, col_types);
+
+       view = gtk_tree_view_new();
+
+       renderer = gtk_cell_renderer_text_new();
+
+       col_idx = 0;
+
+       /* The percentage column */
+       gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
+                                                   -1, "Overhead (%)",
+                                                   renderer, "text",
+                                                   col_idx++, NULL);
+
+       list_for_each_entry(se, &hist_entry__sort_list, list) {
+               if (se->elide)
+                       continue;
+
+               gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
+                                                           -1, se->se_header,
+                                                           renderer, "text",
+                                                           col_idx++, NULL);
+       }
+
+       gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
+
+       g_object_unref(GTK_TREE_MODEL(store));
+
+       total_period = hists->stats.total_period;
+
+       for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+               struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+               GtkTreeIter iter;
+               double percent;
+               char s[512];
+
+               if (h->filtered)
+                       continue;
+
+               gtk_list_store_append(store, &iter);
+
+               col_idx = 0;
+
+               percent = (h->period * 100.0) / total_period;
+
+               snprintf(s, ARRAY_SIZE(s), "%.2f", percent);
+
+               gtk_list_store_set(store, &iter, col_idx++, s, -1);
+
+               list_for_each_entry(se, &hist_entry__sort_list, list) {
+                       if (se->elide)
+                               continue;
+
+                       se->se_snprintf(h, s, ARRAY_SIZE(s),
+                                       hists__col_len(hists, se->se_width_idx));
+
+                       gtk_list_store_set(store, &iter, col_idx++, s, -1);
+               }
+       }
+
+       gtk_container_add(GTK_CONTAINER(window), view);
+}
+
+int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
+                                 const char *help __used,
+                                 void (*timer) (void *arg)__used,
+                                 void *arg __used, int delay_secs __used)
+{
+       struct perf_evsel *pos;
+       GtkWidget *notebook;
+       GtkWidget *window;
+
+       signal(SIGSEGV, perf_gtk_signal);
+       signal(SIGFPE,  perf_gtk_signal);
+       signal(SIGINT,  perf_gtk_signal);
+       signal(SIGQUIT, perf_gtk_signal);
+       signal(SIGTERM, perf_gtk_signal);
+
+       window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+       gtk_window_set_title(GTK_WINDOW(window), "perf report");
+
+       g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
+
+       notebook = gtk_notebook_new();
+
+       list_for_each_entry(pos, &evlist->entries, node) {
+               struct hists *hists = &pos->hists;
+               const char *evname = event_name(pos);
+               GtkWidget *scrolled_window;
+               GtkWidget *tab_label;
+
+               scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+
+               gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                                       GTK_POLICY_AUTOMATIC,
+                                                       GTK_POLICY_AUTOMATIC);
+
+               perf_gtk_show_hists(scrolled_window, hists);
+
+               tab_label = gtk_label_new(evname);
+
+               gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
+       }
+
+       gtk_container_add(GTK_CONTAINER(window), notebook);
+
+       gtk_widget_show_all(window);
+
+       perf_gtk_resize_window(window);
+
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+
+       gtk_main();
+
+       return 0;
+}
diff --git a/tools/perf/util/gtk/gtk.h b/tools/perf/util/gtk/gtk.h
new file mode 100644 (file)
index 0000000..75177ee
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _PERF_GTK_H_
+#define _PERF_GTK_H_ 1
+
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#include <gtk/gtk.h>
+#pragma GCC diagnostic error "-Wstrict-prototypes"
+
+#endif /* _PERF_GTK_H_ */
index fcd9cf3ea63e24b7057a7c007aa12f25ad340e5d..4c7c2d73251f81af31f6394c394baa0d17b64043 100644 (file)
@@ -1177,7 +1177,7 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
                goto error;
 
        msz = sizeof(attr);
-       if (sz < (ssize_t)msz)
+       if (sz < msz)
                msz = sz;
 
        for (i = 0 ; i < nre; i++) {
index 3dc99a9b71f507ea777f8b7d2fc3b1f024a32c39..2ec4b60aff6c1efa5929c53e3c572b1d6504cfbd 100644 (file)
@@ -10,11 +10,14 @@ static bool hists__filter_entry_by_dso(struct hists *hists,
                                       struct hist_entry *he);
 static bool hists__filter_entry_by_thread(struct hists *hists,
                                          struct hist_entry *he);
+static bool hists__filter_entry_by_symbol(struct hists *hists,
+                                         struct hist_entry *he);
 
 enum hist_filter {
        HIST_FILTER__DSO,
        HIST_FILTER__THREAD,
        HIST_FILTER__PARENT,
+       HIST_FILTER__SYMBOL,
 };
 
 struct callchain_param callchain_param = {
@@ -420,6 +423,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
 {
        hists__filter_entry_by_dso(hists, he);
        hists__filter_entry_by_thread(hists, he);
+       hists__filter_entry_by_symbol(hists, he);
 }
 
 static void __hists__collapse_resort(struct hists *hists, bool threaded)
@@ -603,7 +607,7 @@ static void init_rem_hits(void)
        rem_hits.ms.sym = rem_sq_bracket;
 }
 
-static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
                                         u64 total_samples, int depth,
                                         int depth_mask, int left_margin)
 {
@@ -611,21 +615,16 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
        struct callchain_node *child;
        struct callchain_list *chain;
        int new_depth_mask = depth_mask;
-       u64 new_total;
        u64 remaining;
        size_t ret = 0;
        int i;
        uint entries_printed = 0;
 
-       if (callchain_param.mode == CHAIN_GRAPH_REL)
-               new_total = self->children_hit;
-       else
-               new_total = total_samples;
-
-       remaining = new_total;
+       remaining = total_samples;
 
-       node = rb_first(&self->rb_root);
+       node = rb_first(root);
        while (node) {
+               u64 new_total;
                u64 cumul;
 
                child = rb_entry(node, struct callchain_node, rb_node);
@@ -653,11 +652,17 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
                list_for_each_entry(chain, &child->val, list) {
                        ret += ipchain__fprintf_graph(fp, chain, depth,
                                                      new_depth_mask, i++,
-                                                     new_total,
+                                                     total_samples,
                                                      cumul,
                                                      left_margin);
                }
-               ret += __callchain__fprintf_graph(fp, child, new_total,
+
+               if (callchain_param.mode == CHAIN_GRAPH_REL)
+                       new_total = child->children_hit;
+               else
+                       new_total = total_samples;
+
+               ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
                                                  depth + 1,
                                                  new_depth_mask | (1 << depth),
                                                  left_margin);
@@ -667,61 +672,75 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
        }
 
        if (callchain_param.mode == CHAIN_GRAPH_REL &&
-               remaining && remaining != new_total) {
+               remaining && remaining != total_samples) {
 
                if (!rem_sq_bracket)
                        return ret;
 
                new_depth_mask &= ~(1 << (depth - 1));
-
                ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
-                                             new_depth_mask, 0, new_total,
+                                             new_depth_mask, 0, total_samples,
                                              remaining, left_margin);
        }
 
        return ret;
 }
 
-static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
                                       u64 total_samples, int left_margin)
 {
+       struct callchain_node *cnode;
        struct callchain_list *chain;
+       u32 entries_printed = 0;
        bool printed = false;
+       struct rb_node *node;
        int i = 0;
-       int ret = 0;
-       u32 entries_printed = 0;
-
-       list_for_each_entry(chain, &self->val, list) {
-               if (!i++ && sort__first_dimension == SORT_SYM)
-                       continue;
-
-               if (!printed) {
-                       ret += callchain__fprintf_left_margin(fp, left_margin);
-                       ret += fprintf(fp, "|\n");
-                       ret += callchain__fprintf_left_margin(fp, left_margin);
-                       ret += fprintf(fp, "---");
-
-                       left_margin += 3;
-                       printed = true;
-               } else
-                       ret += callchain__fprintf_left_margin(fp, left_margin);
+       int ret;
 
-               if (chain->ms.sym)
-                       ret += fprintf(fp, " %s\n", chain->ms.sym->name);
-               else
-                       ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
+       /*
+        * If have one single callchain root, don't bother printing
+        * its percentage (100 % in fractal mode and the same percentage
+        * than the hist in graph mode). This also avoid one level of column.
+        */
+       node = rb_first(root);
+       if (node && !rb_next(node)) {
+               cnode = rb_entry(node, struct callchain_node, rb_node);
+               list_for_each_entry(chain, &cnode->val, list) {
+                       /*
+                        * If we sort by symbol, the first entry is the same than
+                        * the symbol. No need to print it otherwise it appears as
+                        * displayed twice.
+                        */
+                       if (!i++ && sort__first_dimension == SORT_SYM)
+                               continue;
+                       if (!printed) {
+                               ret += callchain__fprintf_left_margin(fp, left_margin);
+                               ret += fprintf(fp, "|\n");
+                               ret += callchain__fprintf_left_margin(fp, left_margin);
+                               ret += fprintf(fp, "---");
+                               left_margin += 3;
+                               printed = true;
+                       } else
+                               ret += callchain__fprintf_left_margin(fp, left_margin);
+
+                       if (chain->ms.sym)
+                               ret += fprintf(fp, " %s\n", chain->ms.sym->name);
+                       else
+                               ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
 
-               if (++entries_printed == callchain_param.print_limit)
-                       break;
+                       if (++entries_printed == callchain_param.print_limit)
+                               break;
+               }
+               root = &cnode->rb_root;
        }
 
-       ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
-
-       return ret;
+       return __callchain__fprintf_graph(fp, root, total_samples,
+                                         1, 1, left_margin);
 }
 
-static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
-                                     u64 total_samples)
+static size_t __callchain__fprintf_flat(FILE *fp,
+                                       struct callchain_node *self,
+                                       u64 total_samples)
 {
        struct callchain_list *chain;
        size_t ret = 0;
@@ -729,7 +748,7 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
        if (!self)
                return 0;
 
-       ret += callchain__fprintf_flat(fp, self->parent, total_samples);
+       ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
 
 
        list_for_each_entry(chain, &self->val, list) {
@@ -745,44 +764,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
        return ret;
 }
 
-static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
-                                           u64 total_samples, int left_margin,
-                                           FILE *fp)
+static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
+                                     u64 total_samples)
 {
-       struct rb_node *rb_node;
-       struct callchain_node *chain;
        size_t ret = 0;
        u32 entries_printed = 0;
+       struct rb_node *rb_node;
+       struct callchain_node *chain;
 
-       rb_node = rb_first(&he->sorted_chain);
+       rb_node = rb_first(self);
        while (rb_node) {
                double percent;
 
                chain = rb_entry(rb_node, struct callchain_node, rb_node);
                percent = chain->hit * 100.0 / total_samples;
-               switch (callchain_param.mode) {
-               case CHAIN_FLAT:
-                       ret += percent_color_fprintf(fp, "           %6.2f%%\n",
-                                                    percent);
-                       ret += callchain__fprintf_flat(fp, chain, total_samples);
-                       break;
-               case CHAIN_GRAPH_ABS: /* Falldown */
-               case CHAIN_GRAPH_REL:
-                       ret += callchain__fprintf_graph(fp, chain, total_samples,
-                                                       left_margin);
-               case CHAIN_NONE:
-               default:
-                       break;
-               }
+
+               ret = percent_color_fprintf(fp, "           %6.2f%%\n", percent);
+               ret += __callchain__fprintf_flat(fp, chain, total_samples);
                ret += fprintf(fp, "\n");
                if (++entries_printed == callchain_param.print_limit)
                        break;
+
                rb_node = rb_next(rb_node);
        }
 
        return ret;
 }
 
+static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
+                                           u64 total_samples, int left_margin,
+                                           FILE *fp)
+{
+       switch (callchain_param.mode) {
+       case CHAIN_GRAPH_REL:
+               return callchain__fprintf_graph(fp, &he->sorted_chain, he->period,
+                                               left_margin);
+               break;
+       case CHAIN_GRAPH_ABS:
+               return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
+                                               left_margin);
+               break;
+       case CHAIN_FLAT:
+               return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
+               break;
+       case CHAIN_NONE:
+               break;
+       default:
+               pr_err("Bad callchain mode\n");
+       }
+
+       return 0;
+}
+
 void hists__output_recalc_col_len(struct hists *hists, int max_rows)
 {
        struct rb_node *next = rb_first(&hists->entries);
@@ -887,9 +920,9 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s,
                diff = new_percent - old_percent;
 
                if (fabs(diff) >= 0.01)
-                       ret += scnprintf(bf, sizeof(bf), "%+4.2F%%", diff);
+                       scnprintf(bf, sizeof(bf), "%+4.2F%%", diff);
                else
-                       ret += scnprintf(bf, sizeof(bf), " ");
+                       scnprintf(bf, sizeof(bf), " ");
 
                if (sep)
                        ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
@@ -898,9 +931,9 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s,
 
                if (show_displacement) {
                        if (displacement)
-                               ret += scnprintf(bf, sizeof(bf), "%+4ld", displacement);
+                               scnprintf(bf, sizeof(bf), "%+4ld", displacement);
                        else
-                               ret += scnprintf(bf, sizeof(bf), " ");
+                               scnprintf(bf, sizeof(bf), " ");
 
                        if (sep)
                                ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
@@ -1247,6 +1280,37 @@ void hists__filter_by_thread(struct hists *hists)
        }
 }
 
+static bool hists__filter_entry_by_symbol(struct hists *hists,
+                                         struct hist_entry *he)
+{
+       if (hists->symbol_filter_str != NULL &&
+           (!he->ms.sym || strstr(he->ms.sym->name,
+                                  hists->symbol_filter_str) == NULL)) {
+               he->filtered |= (1 << HIST_FILTER__SYMBOL);
+               return true;
+       }
+
+       return false;
+}
+
+void hists__filter_by_symbol(struct hists *hists)
+{
+       struct rb_node *nd;
+
+       hists->nr_entries = hists->stats.total_period = 0;
+       hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+       hists__reset_col_len(hists);
+
+       for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+               struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+               if (hists__filter_entry_by_symbol(hists, h))
+                       continue;
+
+               hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL);
+       }
+}
+
 int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
 {
        return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip);
index 9413f3e31fea8b5c1c305f1d914b7de2d3e12009..2cae9df40e04c0663ba5b2ccc753595b2731626d 100644 (file)
@@ -62,6 +62,7 @@ struct hists {
        const struct thread     *thread_filter;
        const struct dso        *dso_filter;
        const char              *uid_filter_str;
+       const char              *symbol_filter_str;
        pthread_mutex_t         lock;
        struct events_stats     stats;
        u64                     event_stream;
@@ -107,6 +108,7 @@ int hist_entry__annotate(struct hist_entry *self, size_t privsize);
 
 void hists__filter_by_dso(struct hists *hists);
 void hists__filter_by_thread(struct hists *hists);
+void hists__filter_by_symbol(struct hists *hists);
 
 u16 hists__col_len(struct hists *self, enum hist_column col);
 void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
@@ -145,6 +147,23 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
                                  int refresh);
 #endif
 
+#ifdef NO_GTK2_SUPPORT
+static inline
+int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __used,
+                                 const char *help __used,
+                                 void(*timer)(void *arg) __used,
+                                 void *arg __used,
+                                 int refresh __used)
+{
+       return 0;
+}
+
+#else
+int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help,
+                                 void(*timer)(void *arg), void *arg,
+                                 int refresh);
+#endif
+
 unsigned int hists__sort_list_width(struct hists *self);
 
 #endif /* __PERF_HIST_H */
index c7a6f6faf91e4e15e326c7dd03d8aabe4a197c08..5b3a0ef4e2321523563c9da1e4c0449e3be4c013 100644 (file)
 #include "cache.h"
 #include "header.h"
 #include "debugfs.h"
+#include "parse-events-flex.h"
+#include "pmu.h"
+
+#define MAX_NAME_LEN 100
 
 struct event_symbol {
        u8              type;
@@ -19,11 +23,8 @@ struct event_symbol {
        const char      *alias;
 };
 
-enum event_result {
-       EVT_FAILED,
-       EVT_HANDLED,
-       EVT_HANDLED_ALL
-};
+int parse_events_parse(struct list_head *list, struct list_head *list_tmp,
+                      int *idx);
 
 #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
 #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
@@ -354,7 +355,24 @@ const char *__event_name(int type, u64 config)
        return "unknown";
 }
 
-static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
+static int add_event(struct list_head *list, int *idx,
+                    struct perf_event_attr *attr, char *name)
+{
+       struct perf_evsel *evsel;
+
+       event_attr_init(attr);
+
+       evsel = perf_evsel__new(attr, (*idx)++);
+       if (!evsel)
+               return -ENOMEM;
+
+       list_add_tail(&evsel->node, list);
+
+       evsel->name = strdup(name);
+       return 0;
+}
+
+static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size)
 {
        int i, j;
        int n, longest = -1;
@@ -362,58 +380,57 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int
        for (i = 0; i < size; i++) {
                for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
                        n = strlen(names[i][j]);
-                       if (n > longest && !strncasecmp(*str, names[i][j], n))
+                       if (n > longest && !strncasecmp(str, names[i][j], n))
                                longest = n;
                }
-               if (longest > 0) {
-                       *str += longest;
+               if (longest > 0)
                        return i;
-               }
        }
 
        return -1;
 }
 
-static enum event_result
-parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
+int parse_events_add_cache(struct list_head *list, int *idx,
+                          char *type, char *op_result1, char *op_result2)
 {
-       const char *s = *str;
+       struct perf_event_attr attr;
+       char name[MAX_NAME_LEN];
        int cache_type = -1, cache_op = -1, cache_result = -1;
+       char *op_result[2] = { op_result1, op_result2 };
+       int i, n;
 
-       cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
        /*
         * No fallback - if we cannot get a clear cache type
         * then bail out:
         */
+       cache_type = parse_aliases(type, hw_cache,
+                                  PERF_COUNT_HW_CACHE_MAX);
        if (cache_type == -1)
-               return EVT_FAILED;
+               return -EINVAL;
 
-       while ((cache_op == -1 || cache_result == -1) && *s == '-') {
-               ++s;
+       n = snprintf(name, MAX_NAME_LEN, "%s", type);
+
+       for (i = 0; (i < 2) && (op_result[i]); i++) {
+               char *str = op_result[i];
+
+               snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
 
                if (cache_op == -1) {
-                       cache_op = parse_aliases(&s, hw_cache_op,
-                                               PERF_COUNT_HW_CACHE_OP_MAX);
+                       cache_op = parse_aliases(str, hw_cache_op,
+                                                PERF_COUNT_HW_CACHE_OP_MAX);
                        if (cache_op >= 0) {
                                if (!is_cache_op_valid(cache_type, cache_op))
-                                       return EVT_FAILED;
+                                       return -EINVAL;
                                continue;
                        }
                }
 
                if (cache_result == -1) {
-                       cache_result = parse_aliases(&s, hw_cache_result,
+                       cache_result = parse_aliases(str, hw_cache_result,
                                                PERF_COUNT_HW_CACHE_RESULT_MAX);
                        if (cache_result >= 0)
                                continue;
                }
-
-               /*
-                * Can't parse this as a cache op or result, so back up
-                * to the '-'.
-                */
-               --s;
-               break;
        }
 
        /*
@@ -428,20 +445,17 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
        if (cache_result == -1)
                cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
 
-       attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
-       attr->type = PERF_TYPE_HW_CACHE;
-
-       *str = s;
-       return EVT_HANDLED;
+       memset(&attr, 0, sizeof(attr));
+       attr.config = cache_type | (cache_op << 8) | (cache_result << 16);
+       attr.type = PERF_TYPE_HW_CACHE;
+       return add_event(list, idx, &attr, name);
 }
 
-static enum event_result
-parse_single_tracepoint_event(char *sys_name,
-                             const char *evt_name,
-                             unsigned int evt_length,
-                             struct perf_event_attr *attr,
-                             const char **strp)
+static int add_tracepoint(struct list_head *list, int *idx,
+                         char *sys_name, char *evt_name)
 {
+       struct perf_event_attr attr;
+       char name[MAX_NAME_LEN];
        char evt_path[MAXPATHLEN];
        char id_buf[4];
        u64 id;
@@ -452,130 +466,80 @@ parse_single_tracepoint_event(char *sys_name,
 
        fd = open(evt_path, O_RDONLY);
        if (fd < 0)
-               return EVT_FAILED;
+               return -1;
 
        if (read(fd, id_buf, sizeof(id_buf)) < 0) {
                close(fd);
-               return EVT_FAILED;
+               return -1;
        }
 
        close(fd);
        id = atoll(id_buf);
-       attr->config = id;
-       attr->type = PERF_TYPE_TRACEPOINT;
-       *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */
-
-       attr->sample_type |= PERF_SAMPLE_RAW;
-       attr->sample_type |= PERF_SAMPLE_TIME;
-       attr->sample_type |= PERF_SAMPLE_CPU;
-
-       attr->sample_period = 1;
 
+       memset(&attr, 0, sizeof(attr));
+       attr.config = id;
+       attr.type = PERF_TYPE_TRACEPOINT;
+       attr.sample_type |= PERF_SAMPLE_RAW;
+       attr.sample_type |= PERF_SAMPLE_TIME;
+       attr.sample_type |= PERF_SAMPLE_CPU;
+       attr.sample_period = 1;
 
-       return EVT_HANDLED;
+       snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
+       return add_event(list, idx, &attr, name);
 }
 
-/* sys + ':' + event + ':' + flags*/
-#define MAX_EVOPT_LEN  (MAX_EVENT_LENGTH * 2 + 2 + 128)
-static enum event_result
-parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name,
-                               const char *evt_exp, char *flags)
+static int add_tracepoint_multi(struct list_head *list, int *idx,
+                               char *sys_name, char *evt_name)
 {
        char evt_path[MAXPATHLEN];
        struct dirent *evt_ent;
        DIR *evt_dir;
+       int ret = 0;
 
        snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
        evt_dir = opendir(evt_path);
-
        if (!evt_dir) {
                perror("Can't open event dir");
-               return EVT_FAILED;
+               return -1;
        }
 
-       while ((evt_ent = readdir(evt_dir))) {
-               char event_opt[MAX_EVOPT_LEN + 1];
-               int len;
-
+       while (!ret && (evt_ent = readdir(evt_dir))) {
                if (!strcmp(evt_ent->d_name, ".")
                    || !strcmp(evt_ent->d_name, "..")
                    || !strcmp(evt_ent->d_name, "enable")
                    || !strcmp(evt_ent->d_name, "filter"))
                        continue;
 
-               if (!strglobmatch(evt_ent->d_name, evt_exp))
+               if (!strglobmatch(evt_ent->d_name, evt_name))
                        continue;
 
-               len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
-                              evt_ent->d_name, flags ? ":" : "",
-                              flags ?: "");
-               if (len < 0)
-                       return EVT_FAILED;
-
-               if (parse_events(evlist, event_opt, 0))
-                       return EVT_FAILED;
+               ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);
        }
 
-       return EVT_HANDLED_ALL;
+       return ret;
 }
 
-static enum event_result
-parse_tracepoint_event(struct perf_evlist *evlist, const char **strp,
-                      struct perf_event_attr *attr)
+int parse_events_add_tracepoint(struct list_head *list, int *idx,
+                               char *sys, char *event)
 {
-       const char *evt_name;
-       char *flags = NULL, *comma_loc;
-       char sys_name[MAX_EVENT_LENGTH];
-       unsigned int sys_length, evt_length;
-
-       if (debugfs_valid_mountpoint(tracing_events_path))
-               return 0;
-
-       evt_name = strchr(*strp, ':');
-       if (!evt_name)
-               return EVT_FAILED;
-
-       sys_length = evt_name - *strp;
-       if (sys_length >= MAX_EVENT_LENGTH)
-               return 0;
+       int ret;
 
-       strncpy(sys_name, *strp, sys_length);
-       sys_name[sys_length] = '\0';
-       evt_name = evt_name + 1;
+       ret = debugfs_valid_mountpoint(tracing_events_path);
+       if (ret)
+               return ret;
 
-       comma_loc = strchr(evt_name, ',');
-       if (comma_loc) {
-               /* take the event name up to the comma */
-               evt_name = strndup(evt_name, comma_loc - evt_name);
-       }
-       flags = strchr(evt_name, ':');
-       if (flags) {
-               /* split it out: */
-               evt_name = strndup(evt_name, flags - evt_name);
-               flags++;
-       }
-
-       evt_length = strlen(evt_name);
-       if (evt_length >= MAX_EVENT_LENGTH)
-               return EVT_FAILED;
-       if (strpbrk(evt_name, "*?")) {
-               *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */
-               return parse_multiple_tracepoint_event(evlist, sys_name,
-                                                      evt_name, flags);
-       } else {
-               return parse_single_tracepoint_event(sys_name, evt_name,
-                                                    evt_length, attr, strp);
-       }
+       return strpbrk(event, "*?") ?
+              add_tracepoint_multi(list, idx, sys, event) :
+              add_tracepoint(list, idx, sys, event);
 }
 
-static enum event_result
-parse_breakpoint_type(const char *type, const char **strp,
-                     struct perf_event_attr *attr)
+static int
+parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
 {
        int i;
 
        for (i = 0; i < 3; i++) {
-               if (!type[i])
+               if (!type || !type[i])
                        break;
 
                switch (type[i]) {
@@ -589,164 +553,146 @@ parse_breakpoint_type(const char *type, const char **strp,
                        attr->bp_type |= HW_BREAKPOINT_X;
                        break;
                default:
-                       return EVT_FAILED;
+                       return -EINVAL;
                }
        }
+
        if (!attr->bp_type) /* Default */
                attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
 
-       *strp = type + i;
-
-       return EVT_HANDLED;
+       return 0;
 }
 
-static enum event_result
-parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_breakpoint(struct list_head *list, int *idx,
+                               void *ptr, char *type)
 {
-       const char *target;
-       const char *type;
-       char *endaddr;
-       u64 addr;
-       enum event_result err;
-
-       target = strchr(*strp, ':');
-       if (!target)
-               return EVT_FAILED;
-
-       if (strncmp(*strp, "mem", target - *strp) != 0)
-               return EVT_FAILED;
-
-       target++;
-
-       addr = strtoull(target, &endaddr, 0);
-       if (target == endaddr)
-               return EVT_FAILED;
-
-       attr->bp_addr = addr;
-       *strp = endaddr;
+       struct perf_event_attr attr;
+       char name[MAX_NAME_LEN];
 
-       type = strchr(target, ':');
+       memset(&attr, 0, sizeof(attr));
+       attr.bp_addr = (unsigned long) ptr;
 
-       /* If no type is defined, just rw as default */
-       if (!type) {
-               attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
-       } else {
-               err = parse_breakpoint_type(++type, strp, attr);
-               if (err == EVT_FAILED)
-                       return EVT_FAILED;
-       }
+       if (parse_breakpoint_type(type, &attr))
+               return -EINVAL;
 
        /*
         * We should find a nice way to override the access length
         * Provide some defaults for now
         */
-       if (attr->bp_type == HW_BREAKPOINT_X)
-               attr->bp_len = sizeof(long);
+       if (attr.bp_type == HW_BREAKPOINT_X)
+               attr.bp_len = sizeof(long);
        else
-               attr->bp_len = HW_BREAKPOINT_LEN_4;
+               attr.bp_len = HW_BREAKPOINT_LEN_4;
 
-       attr->type = PERF_TYPE_BREAKPOINT;
+       attr.type = PERF_TYPE_BREAKPOINT;
 
-       return EVT_HANDLED;
+       snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw");
+       return add_event(list, idx, &attr, name);
 }
 
-static int check_events(const char *str, unsigned int i)
+static int config_term(struct perf_event_attr *attr,
+                      struct parse_events__term *term)
 {
-       int n;
+       switch (term->type) {
+       case PARSE_EVENTS__TERM_TYPE_CONFIG:
+               attr->config = term->val.num;
+               break;
+       case PARSE_EVENTS__TERM_TYPE_CONFIG1:
+               attr->config1 = term->val.num;
+               break;
+       case PARSE_EVENTS__TERM_TYPE_CONFIG2:
+               attr->config2 = term->val.num;
+               break;
+       case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
+               attr->sample_period = term->val.num;
+               break;
+       case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
+               /*
+                * TODO uncomment when the field is available
+                * attr->branch_sample_type = term->val.num;
+                */
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
 
-       n = strlen(event_symbols[i].symbol);
-       if (!strncasecmp(str, event_symbols[i].symbol, n))
-               return n;
+static int config_attr(struct perf_event_attr *attr,
+                      struct list_head *head, int fail)
+{
+       struct parse_events__term *term;
 
-       n = strlen(event_symbols[i].alias);
-       if (n) {
-               if (!strncasecmp(str, event_symbols[i].alias, n))
-                       return n;
-       }
+       list_for_each_entry(term, head, list)
+               if (config_term(attr, term) && fail)
+                       return -EINVAL;
 
        return 0;
 }
 
-static enum event_result
-parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_numeric(struct list_head *list, int *idx,
+                            unsigned long type, unsigned long config,
+                            struct list_head *head_config)
 {
-       const char *str = *strp;
-       unsigned int i;
-       int n;
-
-       for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
-               n = check_events(str, i);
-               if (n > 0) {
-                       attr->type = event_symbols[i].type;
-                       attr->config = event_symbols[i].config;
-                       *strp = str + n;
-                       return EVT_HANDLED;
-               }
-       }
-       return EVT_FAILED;
+       struct perf_event_attr attr;
+
+       memset(&attr, 0, sizeof(attr));
+       attr.type = type;
+       attr.config = config;
+
+       if (head_config &&
+           config_attr(&attr, head_config, 1))
+               return -EINVAL;
+
+       return add_event(list, idx, &attr,
+                        (char *) __event_name(type, config));
 }
 
-static enum event_result
-parse_raw_event(const char **strp, struct perf_event_attr *attr)
+int parse_events_add_pmu(struct list_head *list, int *idx,
+                        char *name, struct list_head *head_config)
 {
-       const char *str = *strp;
-       u64 config;
-       int n;
-
-       if (*str != 'r')
-               return EVT_FAILED;
-       n = hex2u64(str + 1, &config);
-       if (n > 0) {
-               const char *end = str + n + 1;
-               if (*end != '\0' && *end != ',' && *end != ':')
-                       return EVT_FAILED;
-
-               *strp = end;
-               attr->type = PERF_TYPE_RAW;
-               attr->config = config;
-               return EVT_HANDLED;
-       }
-       return EVT_FAILED;
+       struct perf_event_attr attr;
+       struct perf_pmu *pmu;
+
+       pmu = perf_pmu__find(name);
+       if (!pmu)
+               return -EINVAL;
+
+       memset(&attr, 0, sizeof(attr));
+
+       /*
+        * Configure hardcoded terms first, no need to check
+        * return value when called with fail == 0 ;)
+        */
+       config_attr(&attr, head_config, 0);
+
+       if (perf_pmu__config(pmu, &attr, head_config))
+               return -EINVAL;
+
+       return add_event(list, idx, &attr, (char *) "pmu");
 }
 
-static enum event_result
-parse_numeric_event(const char **strp, struct perf_event_attr *attr)
+void parse_events_update_lists(struct list_head *list_event,
+                              struct list_head *list_all)
 {
-       const char *str = *strp;
-       char *endp;
-       unsigned long type;
-       u64 config;
-
-       type = strtoul(str, &endp, 0);
-       if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
-               str = endp + 1;
-               config = strtoul(str, &endp, 0);
-               if (endp > str) {
-                       attr->type = type;
-                       attr->config = config;
-                       *strp = endp;
-                       return EVT_HANDLED;
-               }
-       }
-       return EVT_FAILED;
+       /*
+        * Called for single event definition. Update the
+        * 'all event' list, and reinit the 'signle event'
+        * list, for next event definition.
+        */
+       list_splice_tail(list_event, list_all);
+       INIT_LIST_HEAD(list_event);
 }
 
-static int
-parse_event_modifier(const char **strp, struct perf_event_attr *attr)
+int parse_events_modifier(struct list_head *list, char *str)
 {
-       const char *str = *strp;
+       struct perf_evsel *evsel;
        int exclude = 0, exclude_GH = 0;
        int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0;
 
-       if (!*str)
+       if (str == NULL)
                return 0;
 
-       if (*str == ',')
-               return 0;
-
-       if (*str++ != ':')
-               return -1;
-
        while (*str) {
                if (*str == 'u') {
                        if (!exclude)
@@ -775,111 +721,62 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)
 
                ++str;
        }
-       if (str < *strp + 2)
-               return -1;
 
-       *strp = str;
+       /*
+        * precise ip:
+        *
+        *  0 - SAMPLE_IP can have arbitrary skid
+        *  1 - SAMPLE_IP must have constant skid
+        *  2 - SAMPLE_IP requested to have 0 skid
+        *  3 - SAMPLE_IP must have 0 skid
+        *
+        *  See also PERF_RECORD_MISC_EXACT_IP
+        */
+       if (precise > 3)
+               return -EINVAL;
 
-       attr->exclude_user   = eu;
-       attr->exclude_kernel = ek;
-       attr->exclude_hv     = eh;
-       attr->precise_ip     = precise;
-       attr->exclude_host   = eH;
-       attr->exclude_guest  = eG;
+       list_for_each_entry(evsel, list, node) {
+               evsel->attr.exclude_user   = eu;
+               evsel->attr.exclude_kernel = ek;
+               evsel->attr.exclude_hv     = eh;
+               evsel->attr.precise_ip     = precise;
+               evsel->attr.exclude_host   = eH;
+               evsel->attr.exclude_guest  = eG;
+       }
 
        return 0;
 }
 
-/*
- * Each event can have multiple symbolic names.
- * Symbolic names are (almost) exactly matched.
- */
-static enum event_result
-parse_event_symbols(struct perf_evlist *evlist, const char **str,
-                   struct perf_event_attr *attr)
+int parse_events(struct perf_evlist *evlist, const char *str, int unset __used)
 {
-       enum event_result ret;
-
-       ret = parse_tracepoint_event(evlist, str, attr);
-       if (ret != EVT_FAILED)
-               goto modifier;
-
-       ret = parse_raw_event(str, attr);
-       if (ret != EVT_FAILED)
-               goto modifier;
+       LIST_HEAD(list);
+       LIST_HEAD(list_tmp);
+       YY_BUFFER_STATE buffer;
+       int ret, idx = evlist->nr_entries;
 
-       ret = parse_numeric_event(str, attr);
-       if (ret != EVT_FAILED)
-               goto modifier;
+       buffer = parse_events__scan_string(str);
 
-       ret = parse_symbolic_event(str, attr);
-       if (ret != EVT_FAILED)
-               goto modifier;
+       ret = parse_events_parse(&list, &list_tmp, &idx);
 
-       ret = parse_generic_hw_event(str, attr);
-       if (ret != EVT_FAILED)
-               goto modifier;
+       parse_events__flush_buffer(buffer);
+       parse_events__delete_buffer(buffer);
 
-       ret = parse_breakpoint_event(str, attr);
-       if (ret != EVT_FAILED)
-               goto modifier;
-
-       fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
-       fprintf(stderr, "Run 'perf list' for a list of valid events\n");
-       return EVT_FAILED;
-
-modifier:
-       if (parse_event_modifier(str, attr) < 0) {
-               fprintf(stderr, "invalid event modifier: '%s'\n", *str);
-               fprintf(stderr, "Run 'perf list' for a list of valid events and modifiers\n");
-
-               return EVT_FAILED;
+       if (!ret) {
+               int entries = idx - evlist->nr_entries;
+               perf_evlist__splice_list_tail(evlist, &list, entries);
+               return 0;
        }
 
+       /*
+        * There are 2 users - builtin-record and builtin-test objects.
+        * Both call perf_evlist__delete in case of error, so we dont
+        * need to bother.
+        */
+       fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
+       fprintf(stderr, "Run 'perf list' for a list of valid events\n");
        return ret;
 }
 
-int parse_events(struct perf_evlist *evlist , const char *str, int unset __used)
-{
-       struct perf_event_attr attr;
-       enum event_result ret;
-       const char *ostr;
-
-       for (;;) {
-               ostr = str;
-               memset(&attr, 0, sizeof(attr));
-               event_attr_init(&attr);
-               ret = parse_event_symbols(evlist, &str, &attr);
-               if (ret == EVT_FAILED)
-                       return -1;
-
-               if (!(*str == 0 || *str == ',' || isspace(*str)))
-                       return -1;
-
-               if (ret != EVT_HANDLED_ALL) {
-                       struct perf_evsel *evsel;
-                       evsel = perf_evsel__new(&attr, evlist->nr_entries);
-                       if (evsel == NULL)
-                               return -1;
-                       perf_evlist__add(evlist, evsel);
-
-                       evsel->name = calloc(str - ostr + 1, 1);
-                       if (!evsel->name)
-                               return -1;
-                       strncpy(evsel->name, ostr, str - ostr);
-               }
-
-               if (*str == 0)
-                       break;
-               if (*str == ',')
-                       ++str;
-               while (isspace(*str))
-                       ++str;
-       }
-
-       return 0;
-}
-
 int parse_events_option(const struct option *opt, const char *str,
                        int unset __used)
 {
@@ -1052,8 +949,6 @@ int print_hwcache_events(const char *event_glob)
        return printed;
 }
 
-#define MAX_NAME_LEN 100
-
 /*
  * Print the help text for the event symbols:
  */
@@ -1102,8 +997,12 @@ void print_events(const char *event_glob)
 
        printf("\n");
        printf("  %-50s [%s]\n",
-               "rNNN (see 'perf list --help' on how to encode it)",
+              "rNNN",
               event_type_descriptors[PERF_TYPE_RAW]);
+       printf("  %-50s [%s]\n",
+              "cpu/t1=v1[,t2=v2,t3 ...]/modifier",
+              event_type_descriptors[PERF_TYPE_RAW]);
+       printf("   (see 'perf list --help' on how to encode it)\n");
        printf("\n");
 
        printf("  %-50s [%s]\n",
@@ -1113,3 +1012,51 @@ void print_events(const char *event_glob)
 
        print_tracepoint_events(NULL, NULL);
 }
+
+int parse_events__is_hardcoded_term(struct parse_events__term *term)
+{
+       return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX;
+}
+
+int parse_events__new_term(struct parse_events__term **_term, int type,
+                          char *config, char *str, long num)
+{
+       struct parse_events__term *term;
+
+       term = zalloc(sizeof(*term));
+       if (!term)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&term->list);
+       term->type = type;
+       term->config = config;
+
+       switch (type) {
+       case PARSE_EVENTS__TERM_TYPE_CONFIG:
+       case PARSE_EVENTS__TERM_TYPE_CONFIG1:
+       case PARSE_EVENTS__TERM_TYPE_CONFIG2:
+       case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
+       case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
+       case PARSE_EVENTS__TERM_TYPE_NUM:
+               term->val.num = num;
+               break;
+       case PARSE_EVENTS__TERM_TYPE_STR:
+               term->val.str = str;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *_term = term;
+       return 0;
+}
+
+void parse_events__free_terms(struct list_head *terms)
+{
+       struct parse_events__term *term, *h;
+
+       list_for_each_entry_safe(term, h, terms, list)
+               free(term);
+
+       free(terms);
+}
index 7e0cbe75d5f1f79560e2b6cbdd2b2205aea1a359..ca069f893381c3b87081a12abcc3031562b83ab8 100644 (file)
@@ -33,6 +33,55 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
 
 #define EVENTS_HELP_MAX (128*1024)
 
+enum {
+       PARSE_EVENTS__TERM_TYPE_CONFIG,
+       PARSE_EVENTS__TERM_TYPE_CONFIG1,
+       PARSE_EVENTS__TERM_TYPE_CONFIG2,
+       PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD,
+       PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
+       PARSE_EVENTS__TERM_TYPE_NUM,
+       PARSE_EVENTS__TERM_TYPE_STR,
+
+       PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX =
+               PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
+};
+
+struct parse_events__term {
+       char *config;
+       union {
+               char *str;
+               long  num;
+       } val;
+       int type;
+
+       struct list_head list;
+};
+
+int parse_events__is_hardcoded_term(struct parse_events__term *term);
+int parse_events__new_term(struct parse_events__term **term, int type,
+                          char *config, char *str, long num);
+void parse_events__free_terms(struct list_head *terms);
+int parse_events_modifier(struct list_head *list __used, char *str __used);
+int parse_events_add_tracepoint(struct list_head *list, int *idx,
+                               char *sys, char *event);
+int parse_events_add_raw(struct perf_evlist *evlist, unsigned long config,
+                        unsigned long config1, unsigned long config2,
+                        char *mod);
+int parse_events_add_numeric(struct list_head *list, int *idx,
+                            unsigned long type, unsigned long config,
+                            struct list_head *head_config);
+int parse_events_add_cache(struct list_head *list, int *idx,
+                          char *type, char *op_result1, char *op_result2);
+int parse_events_add_breakpoint(struct list_head *list, int *idx,
+                               void *ptr, char *type);
+int parse_events_add_pmu(struct list_head *list, int *idx,
+                        char *pmu , struct list_head *head_config);
+void parse_events_update_lists(struct list_head *list_event,
+                              struct list_head *list_all);
+void parse_events_error(struct list_head *list_all,
+                       struct list_head *list_event,
+                       int *idx, char const *msg);
+
 void print_events(const char *event_glob);
 void print_events_type(u8 type);
 void print_tracepoint_events(const char *subsys_glob, const char *event_glob);
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
new file mode 100644 (file)
index 0000000..05d766e
--- /dev/null
@@ -0,0 +1,127 @@
+
+%option prefix="parse_events_"
+
+%{
+#include <errno.h>
+#include "../perf.h"
+#include "parse-events-bison.h"
+#include "parse-events.h"
+
+static int __value(char *str, int base, int token)
+{
+       long num;
+
+       errno = 0;
+       num = strtoul(str, NULL, base);
+       if (errno)
+               return PE_ERROR;
+
+       parse_events_lval.num = num;
+       return token;
+}
+
+static int value(int base)
+{
+       return __value(parse_events_text, base, PE_VALUE);
+}
+
+static int raw(void)
+{
+       return __value(parse_events_text + 1, 16, PE_RAW);
+}
+
+static int str(int token)
+{
+       parse_events_lval.str = strdup(parse_events_text);
+       return token;
+}
+
+static int sym(int type, int config)
+{
+       parse_events_lval.num = (type << 16) + config;
+       return PE_VALUE_SYM;
+}
+
+static int term(int type)
+{
+       parse_events_lval.num = type;
+       return PE_TERM;
+}
+
+%}
+
+num_dec                [0-9]+
+num_hex                0x[a-fA-F0-9]+
+num_raw_hex    [a-fA-F0-9]+
+name           [a-zA-Z_*?][a-zA-Z0-9_*?]*
+modifier_event [ukhp]{1,5}
+modifier_bp    [rwx]
+
+%%
+cpu-cycles|cycles                              { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
+stalled-cycles-frontend|idle-cycles-frontend   { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
+stalled-cycles-backend|idle-cycles-backend     { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
+instructions                                   { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); }
+cache-references                               { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); }
+cache-misses                                   { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); }
+branch-instructions|branches                   { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); }
+branch-misses                                  { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); }
+bus-cycles                                     { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); }
+ref-cycles                                     { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); }
+cpu-clock                                      { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); }
+task-clock                                     { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); }
+page-faults|faults                             { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); }
+minor-faults                                   { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); }
+major-faults                                   { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); }
+context-switches|cs                            { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); }
+cpu-migrations|migrations                      { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); }
+alignment-faults                               { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
+emulation-faults                               { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
+
+L1-dcache|l1-d|l1d|L1-data             |
+L1-icache|l1-i|l1i|L1-instruction      |
+LLC|L2                                 |
+dTLB|d-tlb|Data-TLB                    |
+iTLB|i-tlb|Instruction-TLB             |
+branch|branches|bpu|btb|bpc            |
+node                                   { return str(PE_NAME_CACHE_TYPE); }
+
+load|loads|read                                |
+store|stores|write                     |
+prefetch|prefetches                    |
+speculative-read|speculative-load      |
+refs|Reference|ops|access              |
+misses|miss                            { return str(PE_NAME_CACHE_OP_RESULT); }
+
+       /*
+        * These are event config hardcoded term names to be specified
+        * within xxx/.../ syntax. So far we dont clash with other names,
+        * so we can put them here directly. In case the we have a conflict
+        * in future, this needs to go into '//' condition block.
+        */
+config                 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); }
+config1                        { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); }
+config2                        { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); }
+period                 { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
+branch_type            { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
+
+mem:                   { return PE_PREFIX_MEM; }
+r{num_raw_hex}         { return raw(); }
+{num_dec}              { return value(10); }
+{num_hex}              { return value(16); }
+
+{modifier_event}       { return str(PE_MODIFIER_EVENT); }
+{modifier_bp}          { return str(PE_MODIFIER_BP); }
+{name}                 { return str(PE_NAME); }
+"/"                    { return '/'; }
+-                      { return '-'; }
+,                      { return ','; }
+:                      { return ':'; }
+=                      { return '='; }
+
+%%
+
+int parse_events_wrap(void)
+{
+       return 1;
+}
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
new file mode 100644 (file)
index 0000000..d9637da
--- /dev/null
@@ -0,0 +1,229 @@
+
+%name-prefix "parse_events_"
+%parse-param {struct list_head *list_all}
+%parse-param {struct list_head *list_event}
+%parse-param {int *idx}
+
+%{
+
+#define YYDEBUG 1
+
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include "types.h"
+#include "util.h"
+#include "parse-events.h"
+
+extern int parse_events_lex (void);
+
+#define ABORT_ON(val) \
+do { \
+       if (val) \
+               YYABORT; \
+} while (0)
+
+%}
+
+%token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM
+%token PE_NAME
+%token PE_MODIFIER_EVENT PE_MODIFIER_BP
+%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
+%token PE_PREFIX_MEM PE_PREFIX_RAW
+%token PE_ERROR
+%type <num> PE_VALUE
+%type <num> PE_VALUE_SYM
+%type <num> PE_RAW
+%type <num> PE_TERM
+%type <str> PE_NAME
+%type <str> PE_NAME_CACHE_TYPE
+%type <str> PE_NAME_CACHE_OP_RESULT
+%type <str> PE_MODIFIER_EVENT
+%type <str> PE_MODIFIER_BP
+%type <head> event_config
+%type <term> event_term
+
+%union
+{
+       char *str;
+       unsigned long num;
+       struct list_head *head;
+       struct parse_events__term *term;
+}
+%%
+
+events:
+events ',' event | event
+
+event:
+event_def PE_MODIFIER_EVENT
+{
+       /*
+        * Apply modifier on all events added by single event definition
+        * (there could be more events added for multiple tracepoint
+        * definitions via '*?'.
+        */
+       ABORT_ON(parse_events_modifier(list_event, $2));
+       parse_events_update_lists(list_event, list_all);
+}
+|
+event_def
+{
+       parse_events_update_lists(list_event, list_all);
+}
+
+event_def: event_pmu |
+          event_legacy_symbol |
+          event_legacy_cache sep_dc |
+          event_legacy_mem |
+          event_legacy_tracepoint sep_dc |
+          event_legacy_numeric sep_dc |
+          event_legacy_raw sep_dc
+
+event_pmu:
+PE_NAME '/' event_config '/'
+{
+       ABORT_ON(parse_events_add_pmu(list_event, idx, $1, $3));
+       parse_events__free_terms($3);
+}
+
+event_legacy_symbol:
+PE_VALUE_SYM '/' event_config '/'
+{
+       int type = $1 >> 16;
+       int config = $1 & 255;
+
+       ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, $3));
+       parse_events__free_terms($3);
+}
+|
+PE_VALUE_SYM sep_slash_dc
+{
+       int type = $1 >> 16;
+       int config = $1 & 255;
+
+       ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, NULL));
+}
+
+event_legacy_cache:
+PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
+{
+       ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, $5));
+}
+|
+PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
+{
+       ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, NULL));
+}
+|
+PE_NAME_CACHE_TYPE
+{
+       ABORT_ON(parse_events_add_cache(list_event, idx, $1, NULL, NULL));
+}
+
+event_legacy_mem:
+PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
+{
+       ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, $4));
+}
+|
+PE_PREFIX_MEM PE_VALUE sep_dc
+{
+       ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, NULL));
+}
+
+event_legacy_tracepoint:
+PE_NAME ':' PE_NAME
+{
+       ABORT_ON(parse_events_add_tracepoint(list_event, idx, $1, $3));
+}
+
+event_legacy_numeric:
+PE_VALUE ':' PE_VALUE
+{
+       ABORT_ON(parse_events_add_numeric(list_event, idx, $1, $3, NULL));
+}
+
+event_legacy_raw:
+PE_RAW
+{
+       ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL));
+}
+
+event_config:
+event_config ',' event_term
+{
+       struct list_head *head = $1;
+       struct parse_events__term *term = $3;
+
+       ABORT_ON(!head);
+       list_add_tail(&term->list, head);
+       $$ = $1;
+}
+|
+event_term
+{
+       struct list_head *head = malloc(sizeof(*head));
+       struct parse_events__term *term = $1;
+
+       ABORT_ON(!head);
+       INIT_LIST_HEAD(head);
+       list_add_tail(&term->list, head);
+       $$ = head;
+}
+
+event_term:
+PE_NAME '=' PE_NAME
+{
+       struct parse_events__term *term;
+
+       ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR,
+                $1, $3, 0));
+       $$ = term;
+}
+|
+PE_NAME '=' PE_VALUE
+{
+       struct parse_events__term *term;
+
+       ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
+                $1, NULL, $3));
+       $$ = term;
+}
+|
+PE_NAME
+{
+       struct parse_events__term *term;
+
+       ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
+                $1, NULL, 1));
+       $$ = term;
+}
+|
+PE_TERM '=' PE_VALUE
+{
+       struct parse_events__term *term;
+
+       ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3));
+       $$ = term;
+}
+|
+PE_TERM
+{
+       struct parse_events__term *term;
+
+       ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1));
+       $$ = term;
+}
+
+sep_dc: ':' |
+
+sep_slash_dc: '/' | ':' |
+
+%%
+
+void parse_events_error(struct list_head *list_all __used,
+                       struct list_head *list_event __used,
+                       int *idx __used,
+                       char const *msg __used)
+{
+}
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
new file mode 100644 (file)
index 0000000..cb08a11
--- /dev/null
@@ -0,0 +1,469 @@
+
+#include <linux/list.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include "sysfs.h"
+#include "util.h"
+#include "pmu.h"
+#include "parse-events.h"
+
+int perf_pmu_parse(struct list_head *list, char *name);
+extern FILE *perf_pmu_in;
+
+static LIST_HEAD(pmus);
+
+/*
+ * Parse & process all the sysfs attributes located under
+ * the directory specified in 'dir' parameter.
+ */
+static int pmu_format_parse(char *dir, struct list_head *head)
+{
+       struct dirent *evt_ent;
+       DIR *format_dir;
+       int ret = 0;
+
+       format_dir = opendir(dir);
+       if (!format_dir)
+               return -EINVAL;
+
+       while (!ret && (evt_ent = readdir(format_dir))) {
+               char path[PATH_MAX];
+               char *name = evt_ent->d_name;
+               FILE *file;
+
+               if (!strcmp(name, ".") || !strcmp(name, ".."))
+                       continue;
+
+               snprintf(path, PATH_MAX, "%s/%s", dir, name);
+
+               ret = -EINVAL;
+               file = fopen(path, "r");
+               if (!file)
+                       break;
+
+               perf_pmu_in = file;
+               ret = perf_pmu_parse(head, name);
+               fclose(file);
+       }
+
+       closedir(format_dir);
+       return ret;
+}
+
+/*
+ * Reading/parsing the default pmu format definition, which should be
+ * located at:
+ * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
+ */
+static int pmu_format(char *name, struct list_head *format)
+{
+       struct stat st;
+       char path[PATH_MAX];
+       const char *sysfs;
+
+       sysfs = sysfs_find_mountpoint();
+       if (!sysfs)
+               return -1;
+
+       snprintf(path, PATH_MAX,
+                "%s/bus/event_source/devices/%s/format", sysfs, name);
+
+       if (stat(path, &st) < 0)
+               return -1;
+
+       if (pmu_format_parse(path, format))
+               return -1;
+
+       return 0;
+}
+
+/*
+ * Reading/parsing the default pmu type value, which should be
+ * located at:
+ * /sys/bus/event_source/devices/<dev>/type as sysfs attribute.
+ */
+static int pmu_type(char *name, __u32 *type)
+{
+       struct stat st;
+       char path[PATH_MAX];
+       const char *sysfs;
+       FILE *file;
+       int ret = 0;
+
+       sysfs = sysfs_find_mountpoint();
+       if (!sysfs)
+               return -1;
+
+       snprintf(path, PATH_MAX,
+                "%s/bus/event_source/devices/%s/type", sysfs, name);
+
+       if (stat(path, &st) < 0)
+               return -1;
+
+       file = fopen(path, "r");
+       if (!file)
+               return -EINVAL;
+
+       if (1 != fscanf(file, "%u", type))
+               ret = -1;
+
+       fclose(file);
+       return ret;
+}
+
+static struct perf_pmu *pmu_lookup(char *name)
+{
+       struct perf_pmu *pmu;
+       LIST_HEAD(format);
+       __u32 type;
+
+       /*
+        * The pmu data we store & need consists of the pmu
+        * type value and format definitions. Load both right
+        * now.
+        */
+       if (pmu_format(name, &format))
+               return NULL;
+
+       if (pmu_type(name, &type))
+               return NULL;
+
+       pmu = zalloc(sizeof(*pmu));
+       if (!pmu)
+               return NULL;
+
+       INIT_LIST_HEAD(&pmu->format);
+       list_splice(&format, &pmu->format);
+       pmu->name = strdup(name);
+       pmu->type = type;
+       return pmu;
+}
+
+static struct perf_pmu *pmu_find(char *name)
+{
+       struct perf_pmu *pmu;
+
+       list_for_each_entry(pmu, &pmus, list)
+               if (!strcmp(pmu->name, name))
+                       return pmu;
+
+       return NULL;
+}
+
+struct perf_pmu *perf_pmu__find(char *name)
+{
+       struct perf_pmu *pmu;
+
+       /*
+        * Once PMU is loaded it stays in the list,
+        * so we keep us from multiple reading/parsing
+        * the pmu format definitions.
+        */
+       pmu = pmu_find(name);
+       if (pmu)
+               return pmu;
+
+       return pmu_lookup(name);
+}
+
+static struct perf_pmu__format*
+pmu_find_format(struct list_head *formats, char *name)
+{
+       struct perf_pmu__format *format;
+
+       list_for_each_entry(format, formats, list)
+               if (!strcmp(format->name, name))
+                       return format;
+
+       return NULL;
+}
+
+/*
+ * Returns value based on the format definition (format parameter)
+ * and unformated value (value parameter).
+ *
+ * TODO maybe optimize a little ;)
+ */
+static __u64 pmu_format_value(unsigned long *format, __u64 value)
+{
+       unsigned long fbit, vbit;
+       __u64 v = 0;
+
+       for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) {
+
+               if (!test_bit(fbit, format))
+                       continue;
+
+               if (!(value & (1llu << vbit++)))
+                       continue;
+
+               v |= (1llu << fbit);
+       }
+
+       return v;
+}
+
+/*
+ * Setup one of config[12] attr members based on the
+ * user input data - temr parameter.
+ */
+static int pmu_config_term(struct list_head *formats,
+                          struct perf_event_attr *attr,
+                          struct parse_events__term *term)
+{
+       struct perf_pmu__format *format;
+       __u64 *vp;
+
+       /*
+        * Support only for hardcoded and numnerial terms.
+        * Hardcoded terms should be already in, so nothing
+        * to be done for them.
+        */
+       if (parse_events__is_hardcoded_term(term))
+               return 0;
+
+       if (term->type != PARSE_EVENTS__TERM_TYPE_NUM)
+               return -EINVAL;
+
+       format = pmu_find_format(formats, term->config);
+       if (!format)
+               return -EINVAL;
+
+       switch (format->value) {
+       case PERF_PMU_FORMAT_VALUE_CONFIG:
+               vp = &attr->config;
+               break;
+       case PERF_PMU_FORMAT_VALUE_CONFIG1:
+               vp = &attr->config1;
+               break;
+       case PERF_PMU_FORMAT_VALUE_CONFIG2:
+               vp = &attr->config2;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *vp |= pmu_format_value(format->bits, term->val.num);
+       return 0;
+}
+
+static int pmu_config(struct list_head *formats, struct perf_event_attr *attr,
+                     struct list_head *head_terms)
+{
+       struct parse_events__term *term, *h;
+
+       list_for_each_entry_safe(term, h, head_terms, list)
+               if (pmu_config_term(formats, attr, term))
+                       return -EINVAL;
+
+       return 0;
+}
+
+/*
+ * Configures event's 'attr' parameter based on the:
+ * 1) users input - specified in terms parameter
+ * 2) pmu format definitions - specified by pmu parameter
+ */
+int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
+                    struct list_head *head_terms)
+{
+       attr->type = pmu->type;
+       return pmu_config(&pmu->format, attr, head_terms);
+}
+
+int perf_pmu__new_format(struct list_head *list, char *name,
+                        int config, unsigned long *bits)
+{
+       struct perf_pmu__format *format;
+
+       format = zalloc(sizeof(*format));
+       if (!format)
+               return -ENOMEM;
+
+       format->name = strdup(name);
+       format->value = config;
+       memcpy(format->bits, bits, sizeof(format->bits));
+
+       list_add_tail(&format->list, list);
+       return 0;
+}
+
+void perf_pmu__set_format(unsigned long *bits, long from, long to)
+{
+       long b;
+
+       if (!to)
+               to = from;
+
+       memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS));
+       for (b = from; b <= to; b++)
+               set_bit(b, bits);
+}
+
+/* Simulated format definitions. */
+static struct test_format {
+       const char *name;
+       const char *value;
+} test_formats[] = {
+       { "krava01", "config:0-1,62-63\n", },
+       { "krava02", "config:10-17\n", },
+       { "krava03", "config:5\n", },
+       { "krava11", "config1:0,2,4,6,8,20-28\n", },
+       { "krava12", "config1:63\n", },
+       { "krava13", "config1:45-47\n", },
+       { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
+       { "krava22", "config2:8,18,48,58\n", },
+       { "krava23", "config2:28-29,38\n", },
+};
+
+#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
+
+/* Simulated users input. */
+static struct parse_events__term test_terms[] = {
+       {
+               .config  = (char *) "krava01",
+               .val.num = 15,
+               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+       },
+       {
+               .config  = (char *) "krava02",
+               .val.num = 170,
+               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+       },
+       {
+               .config  = (char *) "krava03",
+               .val.num = 1,
+               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+       },
+       {
+               .config  = (char *) "krava11",
+               .val.num = 27,
+               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+       },
+       {
+               .config  = (char *) "krava12",
+               .val.num = 1,
+               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+       },
+       {
+               .config  = (char *) "krava13",
+               .val.num = 2,
+               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+       },
+       {
+               .config  = (char *) "krava21",
+               .val.num = 119,
+               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+       },
+       {
+               .config  = (char *) "krava22",
+               .val.num = 11,
+               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+       },
+       {
+               .config  = (char *) "krava23",
+               .val.num = 2,
+               .type    = PARSE_EVENTS__TERM_TYPE_NUM,
+       },
+};
+#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
+
+/*
+ * Prepare format directory data, exported by kernel
+ * at /sys/bus/event_source/devices/<dev>/format.
+ */
+static char *test_format_dir_get(void)
+{
+       static char dir[PATH_MAX];
+       unsigned int i;
+
+       snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX");
+       if (!mkdtemp(dir))
+               return NULL;
+
+       for (i = 0; i < TEST_FORMATS_CNT; i++) {
+               static char name[PATH_MAX];
+               struct test_format *format = &test_formats[i];
+               FILE *file;
+
+               snprintf(name, PATH_MAX, "%s/%s", dir, format->name);
+
+               file = fopen(name, "w");
+               if (!file)
+                       return NULL;
+
+               if (1 != fwrite(format->value, strlen(format->value), 1, file))
+                       break;
+
+               fclose(file);
+       }
+
+       return dir;
+}
+
+/* Cleanup format directory. */
+static int test_format_dir_put(char *dir)
+{
+       char buf[PATH_MAX];
+       snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir);
+       if (system(buf))
+               return -1;
+
+       snprintf(buf, PATH_MAX, "rmdir %s\n", dir);
+       return system(buf);
+}
+
+static struct list_head *test_terms_list(void)
+{
+       static LIST_HEAD(terms);
+       unsigned int i;
+
+       for (i = 0; i < TERMS_CNT; i++)
+               list_add_tail(&test_terms[i].list, &terms);
+
+       return &terms;
+}
+
+#undef TERMS_CNT
+
+int perf_pmu__test(void)
+{
+       char *format = test_format_dir_get();
+       LIST_HEAD(formats);
+       struct list_head *terms = test_terms_list();
+       int ret;
+
+       if (!format)
+               return -EINVAL;
+
+       do {
+               struct perf_event_attr attr;
+
+               memset(&attr, 0, sizeof(attr));
+
+               ret = pmu_format_parse(format, &formats);
+               if (ret)
+                       break;
+
+               ret = pmu_config(&formats, &attr, terms);
+               if (ret)
+                       break;
+
+               ret = -EINVAL;
+
+               if (attr.config  != 0xc00000000002a823)
+                       break;
+               if (attr.config1 != 0x8000400000000145)
+                       break;
+               if (attr.config2 != 0x0400000020041d07)
+                       break;
+
+               ret = 0;
+       } while (0);
+
+       test_format_dir_put(format);
+       return ret;
+}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
new file mode 100644 (file)
index 0000000..68c0db9
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __PMU_H
+#define __PMU_H
+
+#include <linux/bitops.h>
+#include "../../../include/linux/perf_event.h"
+
+enum {
+       PERF_PMU_FORMAT_VALUE_CONFIG,
+       PERF_PMU_FORMAT_VALUE_CONFIG1,
+       PERF_PMU_FORMAT_VALUE_CONFIG2,
+};
+
+#define PERF_PMU_FORMAT_BITS 64
+
+struct perf_pmu__format {
+       char *name;
+       int value;
+       DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
+       struct list_head list;
+};
+
+struct perf_pmu {
+       char *name;
+       __u32 type;
+       struct list_head format;
+       struct list_head list;
+};
+
+struct perf_pmu *perf_pmu__find(char *name);
+int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
+                    struct list_head *head_terms);
+
+int perf_pmu_wrap(void);
+void perf_pmu_error(struct list_head *list, char *name, char const *msg);
+
+int perf_pmu__new_format(struct list_head *list, char *name,
+                        int config, unsigned long *bits);
+void perf_pmu__set_format(unsigned long *bits, long from, long to);
+
+int perf_pmu__test(void);
+#endif /* __PMU_H */
diff --git a/tools/perf/util/pmu.l b/tools/perf/util/pmu.l
new file mode 100644 (file)
index 0000000..a15d9fb
--- /dev/null
@@ -0,0 +1,43 @@
+%option prefix="perf_pmu_"
+
+%{
+#include <stdlib.h>
+#include <linux/bitops.h>
+#include "pmu.h"
+#include "pmu-bison.h"
+
+static int value(int base)
+{
+       long num;
+
+       errno = 0;
+       num = strtoul(perf_pmu_text, NULL, base);
+       if (errno)
+               return PP_ERROR;
+
+       perf_pmu_lval.num = num;
+       return PP_VALUE;
+}
+
+%}
+
+num_dec         [0-9]+
+
+%%
+
+{num_dec}      { return value(10); }
+config         { return PP_CONFIG; }
+config1                { return PP_CONFIG1; }
+config2                { return PP_CONFIG2; }
+-              { return '-'; }
+:              { return ':'; }
+,              { return ','; }
+.              { ; }
+\n             { ; }
+
+%%
+
+int perf_pmu_wrap(void)
+{
+       return 1;
+}
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y
new file mode 100644 (file)
index 0000000..20ea77e
--- /dev/null
@@ -0,0 +1,93 @@
+
+%name-prefix "perf_pmu_"
+%parse-param {struct list_head *format}
+%parse-param {char *name}
+
+%{
+
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include <linux/bitmap.h>
+#include <string.h>
+#include "pmu.h"
+
+extern int perf_pmu_lex (void);
+
+#define ABORT_ON(val) \
+do { \
+        if (val) \
+                YYABORT; \
+} while (0)
+
+%}
+
+%token PP_CONFIG PP_CONFIG1 PP_CONFIG2
+%token PP_VALUE PP_ERROR
+%type <num> PP_VALUE
+%type <bits> bit_term
+%type <bits> bits
+
+%union
+{
+       unsigned long num;
+       DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
+}
+
+%%
+
+format:
+format format_term
+|
+format_term
+
+format_term:
+PP_CONFIG ':' bits
+{
+       ABORT_ON(perf_pmu__new_format(format, name,
+                                     PERF_PMU_FORMAT_VALUE_CONFIG,
+                                     $3));
+}
+|
+PP_CONFIG1 ':' bits
+{
+       ABORT_ON(perf_pmu__new_format(format, name,
+                                     PERF_PMU_FORMAT_VALUE_CONFIG1,
+                                     $3));
+}
+|
+PP_CONFIG2 ':' bits
+{
+       ABORT_ON(perf_pmu__new_format(format, name,
+                                     PERF_PMU_FORMAT_VALUE_CONFIG2,
+                                     $3));
+}
+
+bits:
+bits ',' bit_term
+{
+       bitmap_or($$, $1, $3, 64);
+}
+|
+bit_term
+{
+       memcpy($$, $1, sizeof($1));
+}
+
+bit_term:
+PP_VALUE '-' PP_VALUE
+{
+       perf_pmu__set_format($$, $1, $3);
+}
+|
+PP_VALUE
+{
+       perf_pmu__set_format($$, $1, 0);
+}
+
+%%
+
+void perf_pmu_error(struct list_head *list __used,
+                   char *name __used,
+                   char const *msg __used)
+{
+}
index 2cc162d3b78c3b8ed720bff80a3b2171a8673397..d448984ed789c25ff3fc6524bc355d67bc2ad6cb 100644 (file)
@@ -972,10 +972,12 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
        struct dwarf_callback_param *param = data;
        struct probe_finder *pf = param->data;
        struct perf_probe_point *pp = &pf->pev->point;
+       Dwarf_Attribute attr;
 
        /* Check tag and diename */
        if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
-           !die_compare_name(sp_die, pp->function))
+           !die_compare_name(sp_die, pp->function) ||
+           dwarf_attr(sp_die, DW_AT_declaration, &attr))
                return DWARF_CB_OK;
 
        /* Check declared file */
index 002ebbf59f48e84aa69898de786b6227e2402ad7..9412e3b05f6888c9504c7ab6b3dd0effe73de627 100644 (file)
@@ -140,6 +140,7 @@ struct perf_session *perf_session__new(const char *filename, int mode,
        INIT_LIST_HEAD(&self->ordered_samples.sample_cache);
        INIT_LIST_HEAD(&self->ordered_samples.to_free);
        machine__init(&self->host_machine, "", HOST_KERNEL_ID);
+       hists__init(&self->hists);
 
        if (mode == O_RDONLY) {
                if (perf_session__open(self, force) < 0)
index 5dd83c3e2c0c4d61dd02a70f42a46af96fca36a8..c0a028c3ebaf35905e99e69f4cef76e5e343f44f 100644 (file)
@@ -1,6 +1,5 @@
 #include <dirent.h>
 #include <errno.h>
-#include <libgen.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -51,6 +50,8 @@ struct symbol_conf symbol_conf = {
 
 int dso__name_len(const struct dso *dso)
 {
+       if (!dso)
+               return strlen("[unknown]");
        if (verbose)
                return dso->long_name_len;
 
index a4088ced1e64493944b2b7b66514ba05e302fba8..dfd1bd8371a4cba9fc31d164bada0400f0ca01a9 100644 (file)
@@ -722,7 +722,7 @@ static char *event_read_name(void)
 static int event_read_id(void)
 {
        char *token;
-       int id;
+       int id = -1;
 
        if (read_expected_item(EVENT_ITEM, "ID") < 0)
                return -1;
@@ -731,15 +731,13 @@ static int event_read_id(void)
                return -1;
 
        if (read_expect_type(EVENT_ITEM, &token) < 0)
-               goto fail;
+               goto free;
 
        id = strtoul(token, NULL, 0);
-       free_token(token);
-       return id;
 
- fail:
+ free:
        free_token(token);
-       return -1;
+       return id;
 }
 
 static int field_is_string(struct format_field *field)
index 84d761b730c10d9d11b1db8d88bcf0afeacb47a8..6ee82f60feaf2988ba31a0040ad9d85a00183f97 100644 (file)
@@ -49,6 +49,8 @@ int ui_browser__warning(struct ui_browser *browser, int timeout,
                        const char *format, ...);
 int ui_browser__help_window(struct ui_browser *browser, const char *text);
 bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text);
+int ui_browser__input_window(const char *title, const char *text, char *input,
+                            const char *exit_msg, int delay_sec);
 
 void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence);
 unsigned int ui_browser__argv_refresh(struct ui_browser *browser);
index fa530fcc764a015646be7de8522d2979987a452e..d7a1c4afe28b9089ab09bbf5b7abce623e46a544 100644 (file)
@@ -879,6 +879,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
        char *options[16];
        int nr_options = 0;
        int key = -1;
+       char buf[64];
 
        if (browser == NULL)
                return -1;
@@ -933,6 +934,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                        goto zoom_dso;
                case 't':
                        goto zoom_thread;
+               case 's':
+                       if (ui_browser__input_window("Symbol to show",
+                                       "Please enter the name of symbol you want to see",
+                                       buf, "ENTER: OK, ESC: Cancel",
+                                       delay_secs * 2) == K_ENTER) {
+                               self->symbol_filter_str = *buf ? buf : NULL;
+                               hists__filter_by_symbol(self);
+                               hist_browser__reset(browser);
+                       }
+                       continue;
                case K_F1:
                case 'h':
                case '?':
@@ -950,7 +961,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                                        "C             Collapse all callchains\n"
                                        "E             Expand all callchains\n"
                                        "d             Zoom into current DSO\n"
-                                       "t             Zoom into current Thread");
+                                       "t             Zoom into current Thread\n"
+                                       "s             Filter symbol by name");
                        continue;
                case K_ENTER:
                case K_RIGHT:
index 3458b1985761b863311d53f17552e7c61019501a..809eca5707fae1fccee91dcd8a22ef46603f347c 100644 (file)
@@ -16,6 +16,8 @@
 #define K_TAB  '\t'
 #define K_UNTAB        SL_KEY_UNTAB
 #define K_UP   SL_KEY_UP
+#define K_BKSPC 0x7f
+#define K_DEL  SL_KEY_DELETE
 
 /* Not really keys */
 #define K_TIMER         -1
index 45daa7c41dad9812226ba8737a23664709d50cbb..ad4374a16bb08b88c008ca6e79fcf4a5b15903b5 100644 (file)
@@ -69,6 +69,88 @@ int ui__popup_menu(int argc, char * const argv[])
        return popup_menu__run(&menu);
 }
 
+int ui_browser__input_window(const char *title, const char *text, char *input,
+                            const char *exit_msg, int delay_secs)
+{
+       int x, y, len, key;
+       int max_len = 60, nr_lines = 0;
+       static char buf[50];
+       const char *t;
+
+       t = text;
+       while (1) {
+               const char *sep = strchr(t, '\n');
+
+               if (sep == NULL)
+                       sep = strchr(t, '\0');
+               len = sep - t;
+               if (max_len < len)
+                       max_len = len;
+               ++nr_lines;
+               if (*sep == '\0')
+                       break;
+               t = sep + 1;
+       }
+
+       max_len += 2;
+       nr_lines += 8;
+       y = SLtt_Screen_Rows / 2 - nr_lines / 2;
+       x = SLtt_Screen_Cols / 2 - max_len / 2;
+
+       SLsmg_set_color(0);
+       SLsmg_draw_box(y, x++, nr_lines, max_len);
+       if (title) {
+               SLsmg_gotorc(y, x + 1);
+               SLsmg_write_string((char *)title);
+       }
+       SLsmg_gotorc(++y, x);
+       nr_lines -= 7;
+       max_len -= 2;
+       SLsmg_write_wrapped_string((unsigned char *)text, y, x,
+                                  nr_lines, max_len, 1);
+       y += nr_lines;
+       len = 5;
+       while (len--) {
+               SLsmg_gotorc(y + len - 1, x);
+               SLsmg_write_nstring((char *)" ", max_len);
+       }
+       SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
+
+       SLsmg_gotorc(y + 3, x);
+       SLsmg_write_nstring((char *)exit_msg, max_len);
+       SLsmg_refresh();
+
+       x += 2;
+       len = 0;
+       key = ui__getch(delay_secs);
+       while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
+               if (key == K_BKSPC) {
+                       if (len == 0)
+                               goto next_key;
+                       SLsmg_gotorc(y, x + --len);
+                       SLsmg_write_char(' ');
+               } else {
+                       buf[len] = key;
+                       SLsmg_gotorc(y, x + len++);
+                       SLsmg_write_char(key);
+               }
+               SLsmg_refresh();
+
+               /* XXX more graceful overflow handling needed */
+               if (len == sizeof(buf) - 1) {
+                       ui_helpline__push("maximum size of symbol name reached!");
+                       key = K_ENTER;
+                       break;
+               }
+next_key:
+               key = ui__getch(delay_secs);
+       }
+
+       buf[len] = '\0';
+       strncpy(input, buf, len+1);
+       return key;
+}
+
 int ui__question_window(const char *title, const char *text,
                        const char *exit_msg, int delay_secs)
 {
index 555c69a5592ad516f1767ac105c82f6d0af59e1a..adf175f614961cebcb6d1bf89fbe7810471da68d 100644 (file)
@@ -4,11 +4,13 @@ turbostat \- Report processor frequency and idle statistics
 .SH SYNOPSIS
 .ft B
 .B turbostat
+.RB [ "\-s" ]
 .RB [ "\-v" ]
 .RB [ "\-M MSR#" ]
 .RB command
 .br
 .B turbostat
+.RB [ "\-s" ]
 .RB [ "\-v" ]
 .RB [ "\-M MSR#" ]
 .RB [ "\-i interval_sec" ]
@@ -25,6 +27,8 @@ supports an "invariant" TSC, plus the APERF and MPERF MSRs.
 on processors that additionally support C-state residency counters.
 
 .SS Options
+The \fB-s\fP option prints only a 1-line summary for each sample interval.
+.PP
 The \fB-v\fP option increases verbosity.
 .PP
 The \fB-M MSR#\fP option dumps the specified MSR,
@@ -39,13 +43,14 @@ displays the statistics gathered since it was forked.
 .SH FIELD DESCRIPTIONS
 .nf
 \fBpk\fP processor package number.
-\fBcr\fP processor core number.
+\fBcor\fP processor core number.
 \fBCPU\fP Linux CPU (logical processor) number.
+Note that multiple CPUs per core indicate support for Intel(R) Hyper-Threading Technology.
 \fB%c0\fP percent of the interval that the CPU retired instructions.
 \fBGHz\fP average clock rate while the CPU was in c0 state.
 \fBTSC\fP average GHz that the TSC ran during the entire interval.
-\fB%c1, %c3, %c6\fP show the percentage residency in hardware core idle states.
-\fB%pc3, %pc6\fP percentage residency in hardware package idle states.
+\fB%c1, %c3, %c6, %c7\fP show the percentage residency in hardware core idle states.
+\fB%pc2, %pc3, %pc6, %pc7\fP percentage residency in hardware package idle states.
 .fi
 .PP
 .SH EXAMPLE
@@ -53,25 +58,37 @@ Without any parameters, turbostat prints out counters ever 5 seconds.
 (override interval with "-i sec" option, or specify a command
 for turbostat to fork).
 
-The first row of statistics reflect the average for the entire system.
+The first row of statistics is a summary for the entire system.
+Note that the summary is a weighted average.
 Subsequent rows show per-CPU statistics.
 
 .nf
 [root@x980]# ./turbostat
-cr   CPU   %c0   GHz  TSC   %c1    %c3    %c6   %pc3   %pc6
-          0.04 1.62 3.38   0.11   0.00  99.85   0.00  95.07
-  0   0   0.04 1.62 3.38   0.06   0.00  99.90   0.00  95.07
-  0   6   0.02 1.62 3.38   0.08   0.00  99.90   0.00  95.07
-  1   2   0.10 1.62 3.38   0.29   0.00  99.61   0.00  95.07
-  1   8   0.11 1.62 3.38   0.28   0.00  99.61   0.00  95.07
-  2   4   0.01 1.62 3.38   0.01   0.00  99.98   0.00  95.07
-  2  10   0.01 1.61 3.38   0.02   0.00  99.98   0.00  95.07
-  8   1   0.07 1.62 3.38   0.15   0.00  99.78   0.00  95.07
-  8   7   0.03 1.62 3.38   0.19   0.00  99.78   0.00  95.07
-  9   3   0.01 1.62 3.38   0.02   0.00  99.98   0.00  95.07
-  9   9   0.01 1.62 3.38   0.02   0.00  99.98   0.00  95.07
- 10   5   0.01 1.62 3.38   0.13   0.00  99.86   0.00  95.07
- 10  11   0.08 1.62 3.38   0.05   0.00  99.86   0.00  95.07
+cor CPU    %c0  GHz  TSC    %c1    %c3    %c6   %pc3   %pc6
+          0.60 1.63 3.38   2.91   0.00  96.49   0.00  76.64
+  0   0   0.59 1.62 3.38   4.51   0.00  94.90   0.00  76.64
+  0   6   1.13 1.64 3.38   3.97   0.00  94.90   0.00  76.64
+  1   2   0.08 1.62 3.38   0.07   0.00  99.85   0.00  76.64
+  1   8   0.03 1.62 3.38   0.12   0.00  99.85   0.00  76.64
+  2   4   0.01 1.62 3.38   0.06   0.00  99.93   0.00  76.64
+  2  10   0.04 1.62 3.38   0.02   0.00  99.93   0.00  76.64
+  8   1   2.85 1.62 3.38  11.71   0.00  85.44   0.00  76.64
+  8   7   1.98 1.62 3.38  12.58   0.00  85.44   0.00  76.64
+  9   3   0.36 1.62 3.38   0.71   0.00  98.93   0.00  76.64
+  9   9   0.09 1.62 3.38   0.98   0.00  98.93   0.00  76.64
+ 10   5   0.03 1.62 3.38   0.09   0.00  99.87   0.00  76.64
+ 10  11   0.07 1.62 3.38   0.06   0.00  99.87   0.00  76.64
+.fi
+.SH SUMMARY EXAMPLE
+The "-s" option prints the column headers just once,
+and then the one line system summary for each sample interval.
+
+.nf
+[root@x980]# ./turbostat -s
+   %c0  GHz  TSC    %c1    %c3    %c6   %pc3   %pc6
+  0.61 1.89 3.38   5.95   0.00  93.44   0.00  66.33
+  0.52 1.62 3.38   6.83   0.00  92.65   0.00  61.11
+  0.62 1.92 3.38   5.47   0.00  93.91   0.00  67.31
 .fi
 .SH VERBOSE EXAMPLE
 The "-v" option adds verbosity to the output:
@@ -101,33 +118,33 @@ until ^C while the other CPUs are mostly idle:
 
 .nf
 [root@x980 lenb]# ./turbostat cat /dev/zero > /dev/null
-
-^Ccr   CPU   %c0   GHz  TSC   %c1    %c3    %c6   %pc3   %pc6
-           8.49 3.63 3.38  16.23   0.66  74.63   0.00   0.00
-   0   0   1.22 3.62 3.38  32.18   0.00  66.60   0.00   0.00
-   0   6   0.40 3.61 3.38  33.00   0.00  66.60   0.00   0.00
-   1   2   0.11 3.14 3.38   0.19   3.95  95.75   0.00   0.00
-   1   8   0.05 2.88 3.38   0.25   3.95  95.75   0.00   0.00
-   2   4   0.00 3.13 3.38   0.02   0.00  99.98   0.00   0.00
-   2  10   0.00 3.09 3.38   0.02   0.00  99.98   0.00   0.00
-   8   1   0.04 3.50 3.38  14.43   0.00  85.54   0.00   0.00
-   8   7   0.03 2.98 3.38  14.43   0.00  85.54   0.00   0.00
-   9   3   0.00 3.16 3.38 100.00   0.00   0.00   0.00   0.00
-   9   9  99.93 3.63 3.38   0.06   0.00   0.00   0.00   0.00
 10   5   0.01 2.82 3.38   0.08   0.00  99.91   0.00   0.00
 10  11   0.02 3.36 3.38   0.06   0.00  99.91   0.00   0.00
-6.950866 sec
+^C
+cor CPU    %c0  GHz  TSC    %c1    %c3    %c6   %pc3   %pc6
+          8.63 3.64 3.38  14.46   0.49  76.42   0.00   0.00
+  0   0   0.34 3.36 3.38  99.66   0.00   0.00   0.00   0.00
+  0   6  99.96 3.64 3.38   0.04   0.00   0.00   0.00   0.00
+  1   2   0.14 3.50 3.38   1.75   2.04  96.07   0.00   0.00
+  1   8   0.38 3.57 3.38   1.51   2.04  96.07   0.00   0.00
+  2   4   0.01 2.65 3.38   0.06   0.00  99.93   0.00   0.00
+  2  10   0.03 2.12 3.38   0.04   0.00  99.93   0.00   0.00
+  8   1   0.91 3.59 3.38  35.27   0.92  62.90   0.00   0.00
+  8   7   1.61 3.63 3.38  34.57   0.92  62.90   0.00   0.00
+  9   3   0.04 3.38 3.38   0.20   0.00  99.76   0.00   0.00
+  9   9   0.04 3.29 3.38   0.20   0.00  99.76   0.00   0.00
10   5   0.03 3.08 3.38   0.12   0.00  99.85   0.00   0.00
10  11   0.05 3.07 3.38   0.10   0.00  99.85   0.00   0.00
+4.907015 sec
 
 .fi
-Above the cycle soaker drives cpu9 up 3.6 Ghz turbo limit
+Above the cycle soaker drives cpu6 up 3.6 Ghz turbo limit
 while the other processors are generally in various states of idle.
 
-Note that cpu3 is an HT sibling sharing core9
-with cpu9, and thus it is unable to get to an idle state
-deeper than c1 while cpu9 is busy.
+Note that cpu0 is an HT sibling sharing core0
+with cpu6, and thus it is unable to get to an idle state
+deeper than c1 while cpu6 is busy.
 
-Note that turbostat reports average GHz of 3.61, while
-the arithmetic average of the GHz column above is 3.24.
+Note that turbostat reports average GHz of 3.64, while
+the arithmetic average of the GHz column above is lower.
 This is a weighted average, where the weight is %c0.  ie. it is the total number of
 un-halted cycles elapsed per time divided by the number of CPUs.
 .SH NOTES
@@ -167,6 +184,6 @@ http://www.intel.com/products/processor/manuals/
 .SH "SEE ALSO"
 msr(4), vmstat(8)
 .PP
-.SH AUTHORS
+.SH AUTHOR
 .nf
 Written by Len Brown <len.brown@intel.com>
index 310d3dd5e547023ea375f933cb2ad7ca61aee081..ab2f682fd44c6cbc402b4a1ab8cbfd0fa9ce0f2e 100644 (file)
@@ -2,7 +2,7 @@
  * turbostat -- show CPU frequency and C-state residency
  * on modern Intel turbo-capable processors.
  *
- * Copyright (c) 2010, Intel Corporation.
+ * Copyright (c) 2012 Intel Corporation.
  * Len Brown <len.brown@intel.com>
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -19,6 +19,7 @@
  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
@@ -32,6 +33,7 @@
 #include <dirent.h>
 #include <string.h>
 #include <ctype.h>
+#include <sched.h>
 
 #define MSR_TSC        0x10
 #define MSR_NEHALEM_PLATFORM_INFO      0xCE
@@ -49,6 +51,7 @@
 char *proc_stat = "/proc/stat";
 unsigned int interval_sec = 5; /* set with -i interval_sec */
 unsigned int verbose;          /* set with -v */
+unsigned int summary_only;     /* set with -s */
 unsigned int skip_c0;
 unsigned int skip_c1;
 unsigned int do_nhm_cstates;
@@ -68,9 +71,10 @@ unsigned int show_cpu;
 int aperf_mperf_unstable;
 int backwards_count;
 char *progname;
-int need_reinitialize;
 
 int num_cpus;
+cpu_set_t *cpu_mask;
+size_t cpu_mask_size;
 
 struct counters {
        unsigned long long tsc;         /* per thread */
@@ -99,44 +103,76 @@ struct timeval tv_even;
 struct timeval tv_odd;
 struct timeval tv_delta;
 
-unsigned long long get_msr(int cpu, off_t offset)
+/*
+ * cpu_mask_init(ncpus)
+ *
+ * allocate and clear cpu_mask
+ * set cpu_mask_size
+ */
+void cpu_mask_init(int ncpus)
+{
+       cpu_mask = CPU_ALLOC(ncpus);
+       if (cpu_mask == NULL) {
+               perror("CPU_ALLOC");
+               exit(3);
+       }
+       cpu_mask_size = CPU_ALLOC_SIZE(ncpus);
+       CPU_ZERO_S(cpu_mask_size, cpu_mask);
+}
+
+void cpu_mask_uninit()
+{
+       CPU_FREE(cpu_mask);
+       cpu_mask = NULL;
+       cpu_mask_size = 0;
+}
+
+int cpu_migrate(int cpu)
+{
+       CPU_ZERO_S(cpu_mask_size, cpu_mask);
+       CPU_SET_S(cpu, cpu_mask_size, cpu_mask);
+       if (sched_setaffinity(0, cpu_mask_size, cpu_mask) == -1)
+               return -1;
+       else
+               return 0;
+}
+
+int get_msr(int cpu, off_t offset, unsigned long long *msr)
 {
        ssize_t retval;
-       unsigned long long msr;
        char pathname[32];
        int fd;
 
        sprintf(pathname, "/dev/cpu/%d/msr", cpu);
        fd = open(pathname, O_RDONLY);
-       if (fd < 0) {
-               perror(pathname);
-               need_reinitialize = 1;
-               return 0;
-       }
-
-       retval = pread(fd, &msr, sizeof msr, offset);
-       if (retval != sizeof msr) {
-               fprintf(stderr, "cpu%d pread(..., 0x%zx) = %jd\n",
-                       cpu, offset, retval);
-               exit(-2);
-       }
+       if (fd < 0)
+               return -1;
 
+       retval = pread(fd, msr, sizeof *msr, offset);
        close(fd);
-       return msr;
+
+       if (retval != sizeof *msr)
+               return -1;
+
+       return 0;
 }
 
 void print_header(void)
 {
        if (show_pkg)
                fprintf(stderr, "pk");
+       if (show_pkg)
+               fprintf(stderr, " ");
        if (show_core)
-               fprintf(stderr, " cr");
+               fprintf(stderr, "cor");
        if (show_cpu)
                fprintf(stderr, " CPU");
+       if (show_pkg || show_core || show_cpu)
+               fprintf(stderr, " ");
        if (do_nhm_cstates)
-               fprintf(stderr, "    %%c0 ");
+               fprintf(stderr, "   %%c0");
        if (has_aperf)
-               fprintf(stderr, " GHz");
+               fprintf(stderr, "  GHz");
        fprintf(stderr, "  TSC");
        if (do_nhm_cstates)
                fprintf(stderr, "    %%c1");
@@ -147,13 +183,13 @@ void print_header(void)
        if (do_snb_cstates)
                fprintf(stderr, "    %%c7");
        if (do_snb_cstates)
-               fprintf(stderr, "  %%pc2");
+               fprintf(stderr, "   %%pc2");
        if (do_nhm_cstates)
-               fprintf(stderr, "  %%pc3");
+               fprintf(stderr, "   %%pc3");
        if (do_nhm_cstates)
-               fprintf(stderr, "  %%pc6");
+               fprintf(stderr, "   %%pc6");
        if (do_snb_cstates)
-               fprintf(stderr, "  %%pc7");
+               fprintf(stderr, "   %%pc7");
        if (extra_msr_offset)
                fprintf(stderr, "        MSR 0x%x ", extra_msr_offset);
 
@@ -187,6 +223,15 @@ void dump_list(struct counters *cnt)
                dump_cnt(cnt);
 }
 
+/*
+ * column formatting convention & formats
+ * package: "pk" 2 columns %2d
+ * core: "cor" 3 columns %3d
+ * CPU: "CPU" 3 columns %3d
+ * GHz: "GHz" 3 columns %3.2
+ * TSC: "TSC" 3 columns %3.2
+ * percentage " %pc3" %6.2
+ */
 void print_cnt(struct counters *p)
 {
        double interval_float;
@@ -196,39 +241,45 @@ void print_cnt(struct counters *p)
        /* topology columns, print blanks on 1st (average) line */
        if (p == cnt_average) {
                if (show_pkg)
+                       fprintf(stderr, "  ");
+               if (show_pkg && show_core)
                        fprintf(stderr, " ");
                if (show_core)
-                       fprintf(stderr, "    ");
+                       fprintf(stderr, "   ");
                if (show_cpu)
-                       fprintf(stderr, "    ");
+                       fprintf(stderr, " " "   ");
        } else {
                if (show_pkg)
-                       fprintf(stderr, "%d", p->pkg);
+                       fprintf(stderr, "%2d", p->pkg);
+               if (show_pkg && show_core)
+                       fprintf(stderr, " ");
                if (show_core)
-                       fprintf(stderr, "%4d", p->core);
+                       fprintf(stderr, "%3d", p->core);
                if (show_cpu)
-                       fprintf(stderr, "%4d", p->cpu);
+                       fprintf(stderr, " %3d", p->cpu);
        }
 
        /* %c0 */
        if (do_nhm_cstates) {
+               if (show_pkg || show_core || show_cpu)
+                       fprintf(stderr, " ");
                if (!skip_c0)
-                       fprintf(stderr, "%7.2f", 100.0 * p->mperf/p->tsc);
+                       fprintf(stderr, "%6.2f", 100.0 * p->mperf/p->tsc);
                else
-                       fprintf(stderr, "   ****");
+                       fprintf(stderr, "  ****");
        }
 
        /* GHz */
        if (has_aperf) {
                if (!aperf_mperf_unstable) {
-                       fprintf(stderr, "%5.2f",
+                       fprintf(stderr, " %3.2f",
                                1.0 * p->tsc / units * p->aperf /
                                p->mperf / interval_float);
                } else {
                        if (p->aperf > p->tsc || p->mperf > p->tsc) {
-                               fprintf(stderr, " ****");
+                               fprintf(stderr, " ***");
                        } else {
-                               fprintf(stderr, "%4.1f*",
+                               fprintf(stderr, "%3.1f*",
                                        1.0 * p->tsc /
                                        units * p->aperf /
                                        p->mperf / interval_float);
@@ -241,7 +292,7 @@ void print_cnt(struct counters *p)
 
        if (do_nhm_cstates) {
                if (!skip_c1)
-                       fprintf(stderr, "%7.2f", 100.0 * p->c1/p->tsc);
+                       fprintf(stderr, " %6.2f", 100.0 * p->c1/p->tsc);
                else
                        fprintf(stderr, "  ****");
        }
@@ -252,13 +303,13 @@ void print_cnt(struct counters *p)
        if (do_snb_cstates)
                fprintf(stderr, " %6.2f", 100.0 * p->c7/p->tsc);
        if (do_snb_cstates)
-               fprintf(stderr, " %5.2f", 100.0 * p->pc2/p->tsc);
+               fprintf(stderr, " %6.2f", 100.0 * p->pc2/p->tsc);
        if (do_nhm_cstates)
-               fprintf(stderr, " %5.2f", 100.0 * p->pc3/p->tsc);
+               fprintf(stderr, " %6.2f", 100.0 * p->pc3/p->tsc);
        if (do_nhm_cstates)
-               fprintf(stderr, " %5.2f", 100.0 * p->pc6/p->tsc);
+               fprintf(stderr, " %6.2f", 100.0 * p->pc6/p->tsc);
        if (do_snb_cstates)
-               fprintf(stderr, " %5.2f", 100.0 * p->pc7/p->tsc);
+               fprintf(stderr, " %6.2f", 100.0 * p->pc7/p->tsc);
        if (extra_msr_offset)
                fprintf(stderr, "  0x%016llx", p->extra_msr);
        putc('\n', stderr);
@@ -267,12 +318,20 @@ void print_cnt(struct counters *p)
 void print_counters(struct counters *counters)
 {
        struct counters *cnt;
+       static int printed;
+
 
-       print_header();
+       if (!printed || !summary_only)
+               print_header();
 
        if (num_cpus > 1)
                print_cnt(cnt_average);
 
+       printed = 1;
+
+       if (summary_only)
+               return;
+
        for (cnt = counters; cnt != NULL; cnt = cnt->next)
                print_cnt(cnt);
 
@@ -440,31 +499,51 @@ void compute_average(struct counters *delta, struct counters *avg)
        free(sum);
 }
 
-void get_counters(struct counters *cnt)
+int get_counters(struct counters *cnt)
 {
        for ( ; cnt; cnt = cnt->next) {
-               cnt->tsc = get_msr(cnt->cpu, MSR_TSC);
-               if (do_nhm_cstates)
-                       cnt->c3 = get_msr(cnt->cpu, MSR_CORE_C3_RESIDENCY);
-               if (do_nhm_cstates)
-                       cnt->c6 = get_msr(cnt->cpu, MSR_CORE_C6_RESIDENCY);
-               if (do_snb_cstates)
-                       cnt->c7 = get_msr(cnt->cpu, MSR_CORE_C7_RESIDENCY);
-               if (has_aperf)
-                       cnt->aperf = get_msr(cnt->cpu, MSR_APERF);
-               if (has_aperf)
-                       cnt->mperf = get_msr(cnt->cpu, MSR_MPERF);
-               if (do_snb_cstates)
-                       cnt->pc2 = get_msr(cnt->cpu, MSR_PKG_C2_RESIDENCY);
-               if (do_nhm_cstates)
-                       cnt->pc3 = get_msr(cnt->cpu, MSR_PKG_C3_RESIDENCY);
-               if (do_nhm_cstates)
-                       cnt->pc6 = get_msr(cnt->cpu, MSR_PKG_C6_RESIDENCY);
+
+               if (cpu_migrate(cnt->cpu))
+                       return -1;
+
+               if (get_msr(cnt->cpu, MSR_TSC, &cnt->tsc))
+                       return -1;
+
+               if (has_aperf) {
+                       if (get_msr(cnt->cpu, MSR_APERF, &cnt->aperf))
+                               return -1;
+                       if (get_msr(cnt->cpu, MSR_MPERF, &cnt->mperf))
+                               return -1;
+               }
+
+               if (do_nhm_cstates) {
+                       if (get_msr(cnt->cpu, MSR_CORE_C3_RESIDENCY, &cnt->c3))
+                               return -1;
+                       if (get_msr(cnt->cpu, MSR_CORE_C6_RESIDENCY, &cnt->c6))
+                               return -1;
+               }
+
                if (do_snb_cstates)
-                       cnt->pc7 = get_msr(cnt->cpu, MSR_PKG_C7_RESIDENCY);
+                       if (get_msr(cnt->cpu, MSR_CORE_C7_RESIDENCY, &cnt->c7))
+                               return -1;
+
+               if (do_nhm_cstates) {
+                       if (get_msr(cnt->cpu, MSR_PKG_C3_RESIDENCY, &cnt->pc3))
+                               return -1;
+                       if (get_msr(cnt->cpu, MSR_PKG_C6_RESIDENCY, &cnt->pc6))
+                               return -1;
+               }
+               if (do_snb_cstates) {
+                       if (get_msr(cnt->cpu, MSR_PKG_C2_RESIDENCY, &cnt->pc2))
+                               return -1;
+                       if (get_msr(cnt->cpu, MSR_PKG_C7_RESIDENCY, &cnt->pc7))
+                               return -1;
+               }
                if (extra_msr_offset)
-                       cnt->extra_msr = get_msr(cnt->cpu, extra_msr_offset);
+                       if (get_msr(cnt->cpu, extra_msr_offset, &cnt->extra_msr))
+                               return -1;
        }
+       return 0;
 }
 
 void print_nehalem_info(void)
@@ -475,7 +554,7 @@ void print_nehalem_info(void)
        if (!do_nehalem_platform_info)
                return;
 
-       msr = get_msr(0, MSR_NEHALEM_PLATFORM_INFO);
+       get_msr(0, MSR_NEHALEM_PLATFORM_INFO, &msr);
 
        ratio = (msr >> 40) & 0xFF;
        fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n",
@@ -491,7 +570,7 @@ void print_nehalem_info(void)
        if (!do_nehalem_turbo_ratio_limit)
                return;
 
-       msr = get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT);
+       get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT, &msr);
 
        ratio = (msr >> 24) & 0xFF;
        if (ratio)
@@ -557,7 +636,8 @@ void insert_counters(struct counters **list,
                return;
        }
 
-       show_cpu = 1;   /* there is more than one CPU */
+       if (!summary_only)
+               show_cpu = 1;   /* there is more than one CPU */
 
        /*
         * insert on front of list.
@@ -575,13 +655,15 @@ void insert_counters(struct counters **list,
 
        while (prev->next && (prev->next->pkg < new->pkg)) {
                prev = prev->next;
-               show_pkg = 1;   /* there is more than 1 package */
+               if (!summary_only)
+                       show_pkg = 1;   /* there is more than 1 package */
        }
 
        while (prev->next && (prev->next->pkg == new->pkg)
                && (prev->next->core < new->core)) {
                prev = prev->next;
-               show_core = 1;  /* there is more than 1 core */
+               if (!summary_only)
+                       show_core = 1;  /* there is more than 1 core */
        }
 
        while (prev->next && (prev->next->pkg == new->pkg)
@@ -681,7 +763,7 @@ int get_core_id(int cpu)
 }
 
 /*
- * run func(index, cpu) on every cpu in /proc/stat
+ * run func(pkg, core, cpu) on every cpu in /proc/stat
  */
 
 int for_all_cpus(void (func)(int, int, int))
@@ -717,18 +799,18 @@ int for_all_cpus(void (func)(int, int, int))
 
 void re_initialize(void)
 {
-       printf("turbostat: topology changed, re-initializing.\n");
        free_all_counters();
        num_cpus = for_all_cpus(alloc_new_counters);
-       need_reinitialize = 0;
-       printf("num_cpus is now %d\n", num_cpus);
+       cpu_mask_uninit();
+       cpu_mask_init(num_cpus);
+       printf("turbostat: re-initialized with num_cpus %d\n", num_cpus);
 }
 
 void dummy(int pkg, int core, int cpu) { return; }
 /*
  * check to see if a cpu came on-line
  */
-void verify_num_cpus(void)
+int verify_num_cpus(void)
 {
        int new_num_cpus;
 
@@ -738,8 +820,9 @@ void verify_num_cpus(void)
                if (verbose)
                        printf("num_cpus was %d, is now  %d\n",
                                num_cpus, new_num_cpus);
-               need_reinitialize = 1;
+               return -1;
        }
+       return 0;
 }
 
 void turbostat_loop()
@@ -749,25 +832,25 @@ restart:
        gettimeofday(&tv_even, (struct timezone *)NULL);
 
        while (1) {
-               verify_num_cpus();
-               if (need_reinitialize) {
+               if (verify_num_cpus()) {
                        re_initialize();
                        goto restart;
                }
                sleep(interval_sec);
-               get_counters(cnt_odd);
+               if (get_counters(cnt_odd)) {
+                       re_initialize();
+                       goto restart;
+               }
                gettimeofday(&tv_odd, (struct timezone *)NULL);
-
                compute_delta(cnt_odd, cnt_even, cnt_delta);
                timersub(&tv_odd, &tv_even, &tv_delta);
                compute_average(cnt_delta, cnt_average);
                print_counters(cnt_delta);
-               if (need_reinitialize) {
+               sleep(interval_sec);
+               if (get_counters(cnt_even)) {
                        re_initialize();
                        goto restart;
                }
-               sleep(interval_sec);
-               get_counters(cnt_even);
                gettimeofday(&tv_even, (struct timezone *)NULL);
                compute_delta(cnt_even, cnt_odd, cnt_delta);
                timersub(&tv_even, &tv_odd, &tv_delta);
@@ -953,6 +1036,7 @@ void turbostat_init()
        check_super_user();
 
        num_cpus = for_all_cpus(alloc_new_counters);
+       cpu_mask_init(num_cpus);
 
        if (verbose)
                print_nehalem_info();
@@ -1005,8 +1089,11 @@ void cmdline(int argc, char **argv)
 
        progname = argv[0];
 
-       while ((opt = getopt(argc, argv, "+vi:M:")) != -1) {
+       while ((opt = getopt(argc, argv, "+svi:M:")) != -1) {
                switch (opt) {
+               case 's':
+                       summary_only++;
+                       break;
                case 'v':
                        verbose++;
                        break;